አንዳንድ Erlang / OTP ሞዴሎች ለ ኮድ አብዛኞቹ ዘመናዊ ሰኔ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ ዲግሪ አግኙን Before we move on to discussing warnings, I'd like to say a few words about the project. Erlang የጉዞ በ 1986 በ Ericsson ውስጥ አንድ የሙከራ ውስጥ ተጀመረ. Joe Armstrong አንድ የቴሌፎኒካዊ ፕሮግራም ለመፍጠር ተሞክሮ ጊዜ Prolog ጋር ተሞክሮ ነበር. እነዚህ ተሞክሮዎች በመጨረሻው Erlang ተሞክሮ ነበር. ይህ የፕሮግራም ቋንቋ በይፋ-የተቀየቅ ስርዓቶች ላይ የተመሠረተ ነው. በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ወይም በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ ስርዓቶች ውስጥ በይፋ-የተቀየቅ አረንጓዴ የኦሪጂናል ከባድ ጋር ይሰራል ይህ መተግበሪያ መተግበሪያዎችን ለመፍጠር ጠንካራ መሣሪያዎች, ሞዴሎች እና ክወናዎችን ያቀርባል. ይህ የተመሠረተ መሣሪያዎች መሣሪያዎች በዚያ ላይ የተመሠረተ ስርዓቶች ለሁሉም ዓመታት በተመሳሳይ መንገድ ይሰራሉ. የ Open Telecom Platform (OTP) አጠቃቀም የፕሮጀክቱ በዚያ ላይ የተገነባው እኛ በኤስ.ኤስ.ኤስ. አጠቃቀም እና የ እኛ የፕሮጀክት ለመፍጠር ጋር ምንም ችግሮች ጋር ተስማሚ ነበር, ስለዚህ ተስማሚዎች ወደ. መመሪያዎች maint-28 የ PVS-Studio ማጣሪያዎች የ Visual Studio ኮድ ይህ ጽሑፍ ብቻ ከፍተኛ እና መካከለኛ ደረጃ የአየር ማረጋገጫዎች ያካትታል. ይህ የእኛን መተግበሪያ መጨረሻ ነው. አሁን, የእኛን መተግበሪያዎች ወደ ይጎብኙ. እኛ እነዚህን በሦስት ክፍል ይሸፍናል: የ Logic Errors አጠቃቀም ትክክለኛ ስህተት; የሙዚቃ ስሜት. የ Logic Errors Fragment N1 የ PVS-Studio ትዕዛዞች: '(!esock_is_integer((env), (argv[3])))' በ ' Átha Cliath' አጠቃቀም ላይ ከባድ እና ከባድ ላይ ተመሳሳይ አጠቃቀሞች አሉ. የ V501 አግኝቷል ፡፡ ፡፡ 6035 static ERL_NIF_TERM nif_sendfile(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { .... BOOLEAN_T a2ok; if ((! (a2ok = GET_INT64(env, argv[2], &offset64))) || (! GET_UINT64(env, argv[3], &count64u))) { if ((! IS_INTEGER(env, argv[3])) || (! IS_INTEGER(env, argv[3]))) return enif_make_badarg(env); if (! a2ok) return esock_make_error_integer_range(env, argv[2]); else return esock_make_error_integer_range(env, argv[3]); } .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት በኮድ ውስጥ, የ 3 ኛ እና 4 ኛ መጋቢዎች ወደ 64-ቢት አይነት (በመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመመ የተመሠረተ ኮድ: .... if ((! IS_INTEGER(env, argv[2])) || (! IS_INTEGER(env, argv[3]))) return enif_make_badarg(env); .... መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት Fragment N2 የ PVS-Studio ትዕዛዞች: There are identical sub-expressions 'n->type == ycf_node_type_on_save_yield_state_code' to the left and to the right of the '||' operator. የ V501 የኮምፒዩተር መለያዎች 218 static void uniqify_local_vars_in_node(ycf_node* n) { if (n->type == ycf_node_type_code_scope) { uniqify_local_vars_in_scope(&n->u.code_scope); } else if(n->type == ycf_node_type_on_restore_yield_state_code) { uniqify_local_vars_in_scope(&n->u.special_code_block .code.if_statement->u.code_scope); } else if (n->type == ycf_node_type_on_save_yield_state_code || n->type == ycf_node_type_on_save_yield_state_code || n->type == ycf_node_type_on_destroy_state_code || n->type == ycf_node_type_on_return_code || n->type == ycf_node_type_on_destroy_state_or_return_code) { uniqify_local_vars_in_scope(&n->u.special_code_block .code.if_statement->u.code_scope); } .... } Enter fullscreen mode Exit fullscreen mode እዚህ ሌላ አስደናቂ የኮፒ-ፓስ ምሳሌ ነው. እኛ አላቸው የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች እና የፕላስቲክ መሣሪያዎች ያቀርባል. branch, the condition is duplicated after the አጠቃቀም uniqify_local_vars_in_node else if n->type == ycf_node_type_on_save_yield_state_code || Unfortunately, I don't have a proper fix for this fragment. Judging by the comparison chain, the values of the following list are sorted in the order they're declared: typedef enum { .... ycf_node_type_on_save_yield_state_code, ycf_node_type_on_restore_yield_state_code, ycf_node_type_on_destroy_state_code, ycf_node_type_on_return_code, ycf_node_type_on_destroy_state_or_return_code } ycf_node_type; መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት ነገር ግን የ በባቡር ውስጥ በባቡር በባቡር በባቡር በባቡር በባቡር በባቡር በባቡር . So, I guess there's a duplicate check here after all, and we can delete it. ycf_node_type_on_restore_yield_state_code else if Fragment N3 የ PVS-Studio ትዕዛዞች: የ Macro ስምዎን ከባድ መለያዎች ውስጥ ሊሆን ይችላል. The ' 'Macro ተመሳሳይ ነው' አግኙን አማርኛ1040 WIN_32 WIN32 አግኝቷል 13506 static int tcp_send_error(tcp_descriptor* desc, int err) { /* EPIPE errors usually occur in one of three ways: * 1. We write to a socket when we've already shutdown() the write side. * On Windows the error returned for this is ESHUTDOWN * rather than EPIPE. * 2. The TCP peer sends us an RST through no fault of our own (perhaps * by aborting the connection using SO_LINGER) and we then attempt * to write to the socket. On Linux and Windows we would actually * receive an ECONNRESET error for this, but on the BSDs, Darwin, * Illumos and presumably Solaris, it's an EPIPE. * 3. We cause the TCP peer to send us an RST by writing to a socket * after we receive a FIN from them. Our first write will be * successful, but if the they have closed the connection (rather * than just shutting down the write side of it) this will cause their * OS to send us an RST. Then, when we attempt to write to the socket * a second time, we will get an EPIPE error. On Windows we get an * ECONNABORTED. * * What we are going to do here is to treat all EPIPE messages that aren't * of type 1 as ECONNRESET errors. This will allow users who have the * show_econnreset socket option enabled to receive {error, econnreset} on * both send and recv operations to indicate that an RST * has been received. */ #ifdef __WIN_32__ if (err == ECONNABORTED) err = ECONNRESET; #endif if (err == EPIPE && !(desc->tcp_add_flags & TCP_ADDF_SHUTDOWN_WR_DONE)) err = ECONNRESET; return tcp_send_or_shutdown_error(desc, err); } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት This is a rather unusual case of checking whether a project compiles under Windows. Erlang has other checks on Windows ([ በ [ ] በ [ ] ይህ በጣም ተመሳሳይ ነው, ነገር ግን ይህ ልዩ ነው. ይህ በጣም ቀላል አንድ typo እንደ ይሆናል (በመደበኛው, ይህ መሆን አለበት አግኙን 1 2 3 __WIN32__ ነገር ግን ይህ ምን ነው? በጉጉጉት, እኔ የሚፈልጉት ይመልከቱ ምን ማግኘት ይችላሉ. እዚህ ውጤቶች ናቸው: "__WIN_32__" እርስዎ መመልከት ይችላሉ, ምናልባት ምንም ውጤቶች አሉ. አንድ አገናኝ እኛን ይመልከቱ ፋይሎችን ወደ ያደርጋል አስደናቂ ነው እኔ የፕሮጀክት ለማግኘት ተመሳሳይ ማክሮ ትዕዛዞች ማግኘት አይችልም. በተጨማሪም ይህ እንደ MinGW እንደ አንዳንድ ኮምፒውተር ውስጥ የተመሠረተ ሊሆን ይችላል. የክፍያ ማክሮ መውሰድ በኋላ, ይህ ኮድ ስህተት ነው ያውቃል. Debian ምንጮች ፈተናዎች እንደ መተግበሪያ, እኔ የፕሮጀክቱ ሌሎች ክፍሎች ውስጥ የቀድሞው አግኝቷል መቆጣጠሪያ ይጠቀማል: static int tcp_send_error(tcp_descriptor* desc, int err) { .... #if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_) if (err == ECONNABORTED) err = ECONNRESET; #endif .... } Enter fullscreen mode Exit fullscreen mode Fragment N4 The PVS-Studio warning: የኮድ አጠቃቀም ዝርዝሮች የኮድ አጠቃቀም ዝርዝሮች ጋር ተስማሚ አይሆንም. የኮድ አጠቃቀም አጠቃቀም አጠቃቀም አጠቃቀም ይሆናል. የ V640 አግኝቷል 246 int main(int argc, char** argv) { .... if (argc > 2 && strcmp(argv[1], "+P") == 0) { PUSH2("+P", argv[2]); argc--, argv++; argc--, argv++; } else PUSH2("+P", "1000000"); .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት Here's an interesting example of a common coding error that can be difficult to spot later on. Take a look at the መዋቅር : PUSH2 #define QUOTE(s) s #define PUSH(s) eargv[eargc++] = QUOTE(s) #define PUSH2(s, t) PUSH(s); PUSH(t) መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት ይህ ኮድ ከሁለት መዋቅር ውስጥ ይጨምራል. አንድ preprocessor እንደ ኮድ ይጨምራል: int main(int argc, char** argv) { .... if (argc > 2 && strcmp(argv[1], "+P") == 0) { eargv[eargc++] = "+P"; eargv[eargc++] = argv[2]; argc--, argv++; argc--, argv++; } else eargv[eargc++] = "+P"; eargv[eargc++] = "1000000"; .... } Enter fullscreen mode Exit fullscreen mode ከሁሉም ጊዜ, የኮምፒውተር ማረጋገጫ , ብቻ የመጀመሪያው ትዕዛዞች መስፈርት ይሰራል, እና ሁለተኛው አንድ ሁልጊዜ ከባድ በኋላ ይሆናል. ይህ መተግበሪያዎች የሚፈልጉት ነገር አይደለም. false ማክሮዎችን መጠቀም እና መፍጠር የተሻለ መፍትሔ እነሱን ለመፍጠር ይሆናል, ነገር ግን አንድ ዝቅተኛ ትክክለኛ ማሻሻያ ለማግኘት ይሆናል. አንድ ማክሮ ውስጥ በርካታ እንቅስቃሴዎችን አንድ ውስጥ ለመፍጠር ያስፈልጋቸዋል. bugs ያግኙን do {...} while (0) #define PUSH2(s, t) do { PUSH(s); PUSH(t); } while (0) መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት Fragment N5 የ PVS-Studio ትዕዛዞች: The condition 'i >= 0' of loop is always true. የ 654 wxe_return.cpp 236 ERL_NIF_TERM wxeReturn::make_array_objs(wxAuiPaneInfoArray& arr, WxeApp *app, const char *cname) { ERL_NIF_TERM head, tail; ERL_NIF_TERM class_name = enif_make_atom(env, cname); tail = enif_make_list(env, 0); for (unsigned int i = arr.GetCount() - 1; i >= 0; i--) { head = make_ref(app->getRef((void *) &arr.Item(i),memenv), class_name); tail = enif_make_list_cell(env, head, tail); } return tail; } Enter fullscreen mode Exit fullscreen mode እባክዎ ይመልከቱ መኖሪያ ቤት, የቤት ውስጥ ከባድ, ከባድ, ከባድ, ከባድ ምናልባት ምናልባት ምናልባት ምናልባት ይሆናል. type with a value range of ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ የ variable ተመሳሳይ ሊሆን ይችላል ከባድ ውርዶች በኋላ, እና ቀዝቃዛ ይጀምራል. for i unsigned int [0; UINT_MAX] i == 0 UINT_MAX Let's fix the code as follows: for (int64_t i = arr.GetCount() - 1; i >= 0; i--) { .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት ተመሳሳይ ጥያቄዎች: V654 የ ሉህ ሁኔታ 'i >= 0' ሁልጊዜ ትክክለኛ ነው. wxe_return.cpp 248 V654 የ ሉህ ሁኔታ 'i >= 0' ሁልጊዜ ትክክለኛ ነው. wxe_return.cpp 260 Fragment N6 የ PVS-Studio ትዕዛዞች: የ "የ (A) {...} ሌሎች በ (A) {...}" ሞዴል መጠቀም የተመሠረተ ነው. የ Logical error presence probability. Check lines: 2903, 3053. የ 517 አግኙን _ info_info.c 2903 BIF_RETTYPE system_info_1(BIF_ALIST_1) { .... if (is_tuple(BIF_ARG_1)) { // L2778 .... } else if (BIF_ARG_1 == am_scheduler_id) { // L2782 .... } .... else if (BIF_ARG_1 == am_garbage_collection) { // L2903 .... } else if (BIF_ARG_1 == am_fullsweep_after) { // L2921 .... } else if (BIF_ARG_1 == am_garbage_collection) { // L3053 .... } else if (BIF_ARG_1 == am_instruction_counts) { // L3056 .... } .... else if (ERTS_IS_ATOM_STR("halt_flush_timeout", BIF_ARG_1)) { // L3552 .... } } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት የኦሪጂናል መቆጣጠሪያው በከፍተኛ መጠን ያለው ተግባር ውስጥ ተመሳሳይ መቆጣጠሪያዎች ጋር በርካታ ቅርንጫፎች ያካትታል. statements—roughly ነገር ግን እያንዳንዱ ሰው የተለያዩ መግቢያ አለው: የ እና . Given the number of branches and the 150-line gap between the duplicates, it's hardly surprising that this could happen. Static analysis helps prevent such cases. Unfortunately, I can't think of a fix, so I'll leave that to the developers. if-else if 800 ቀናት first check ሁለተኛ ቁጥጥር Fragment N7 የ PVS-Studio ትዕዛዞች: እያንዳንዱ ጥንካሬ ቅርጽ አንድ ክፍል ሁልጊዜ ምትክ ነው: (arity == 0). የ V560 ግምገማዎች 3145 BIF_RETTYPE delete_element_2(BIF_ALIST_3) { .... ptr = tuple_val(BIF_ARG_2); arity = arityval(*ptr); ix = signed_val(BIF_ARG_1); if ((ix < 1) || (ix > arity) || (arity == 0)) { BIF_ERROR(BIF_P, BADARG); } .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት እዚህ አንድ ምሳሌ ያቀርባል. የ analyzer እኛን ያውቃል ምልክት መለያዎች ሁልጊዜ ምልክት ነው, ነገር ግን እንዴት ነው? arity == 0 Let's dig into it. If the control flow reaches the point where this subexpression is evaluated, we will know that two previous subexpressions are በተጨማሪም የሚፈልጉትን ነገር ማወቅ ይችላሉ: false ix >= 1 ix <= arity መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት እነዚህን ምልክቶች በሜታሚካዊ ደረጃ ይመዝገቡ: 1 <= ix <= arity መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት Next, we bring up a በይነገጽ ነገር ግን, በእርግጥ በእርግጥ በእርግጥ በእርግጥ በእርግጥ በእርግጥ በእርግጥ ሊሆን ይችላል. transitional relationship ግምገማዎች arity >= 1 arity እኔ ደግሞ ይህን ኮድ ለመፍጠር ወደ የፈጠራዎች ይሰጣል, ምክንያቱም እኔ ብቻ ትዕዛዞች ይህ ሁኔታ ለመውሰድ ነው. ትክክለኛ ስህተት Fragment N8 የ PVS-Studio ይመዝገቡ: Array overrun is possible. The value of 'prefix_len' index could reach 262. V557 አግኝቷል 575 Array overrun is possible. The value of 'prefix_len' index could reach 262. የ 557 አግኝቷል 578 Array overrun is possible. The value of 'prefix_len' index could reach 262. V557 አግኝቷል 5381 #define MAX_ATOM_CHARACTERS 255 static void make_name_atoms(Allctr_t *allctr) { char alloc[] = "alloc"; char realloc[] = "realloc"; char free[] = "free"; char buf[MAX_ATOM_CHARACTERS]; size_t prefix_len = sys_strlen(allctr->name_prefix); if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1) erts_exit(ERTS_ERROR_EXIT, "Too long allocator name: %salloc\n" allctr->name_prefix); sys_memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len); sys_memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1); allctr->name.alloc = am_atom_put(buf, prefix_len + sizeof(alloc) - 1); sys_memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1); allctr->name.realloc = am_atom_put(buf, prefix_len + sizeof(realloc) - 1); sys_memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1); allctr->name.free = am_atom_put(buf, prefix_len + sizeof(free) - 1); } Enter fullscreen mode Exit fullscreen mode የእኛን ተግባር ላይ የተመሠረተ ቅርንጫፍ ላይ የተመሠረተ ቅርንጫፍ ላይ የተመሠረተ ቅርንጫፍ እና ቅርንጫፍ ቅርንጫፍ ስምዎችን ያደርጋል. አንድ 255-ቢት ፒንፍር ይጠቀማል እነዚህን ቅርንጫፍ ያደርጋል. ቅርንጫፍ ቁሳቁሶች መሠረት, የተመሠረተ ቅርንጫፍ ውስጥ ምንም null terminator ይሆናል, ምክንያቱም የፈጠራው አጠቃላይ መጠን ያደርጋል. am_atom_put የፕሮግራፊዎች የመጀመሪያው ነገር የፕሮግራፊ መጠን ያካትታል. ከዚያም, የፕሮግራፊ መጠን ያካትታል: ይህ የፕሮግራፊ መጠን ያካትታል. የጊዜው ርዝመት የቅርብ ጊዜው (አንድ ጊዜው) function is called. MAX_ATOM_CHARACTERS realloc noreturn Next, they copied the prefix to the buffer, added the postfix, and passed the string to the የ Developers ለሁሉም postfixes ለሁሉም እነዚህን እንቅስቃሴዎች ይሰራሉ: , እና . am_atom_put alloc realloc free Can you spot the catch? The early return check was performed incorrectly. The buffer must have enough space to write the longest postfix, which is seven characters. If the prefix string is between 256 and 262 characters long, the early return doesn't happen, so the ይህ አጠቃቀም አጠቃቀም አጠቃቀም አጠቃቀም አጠቃቀም ነው. sys_memcpy አግኝቷል የ ተግባር ከባድ ከባድ ከባድ ከባድ ከባድ የክፍያ ኮድ እንደዚህ ይመልከቱ: am_atom_put MAX_ATOM_CHARACTERS if (prefix_len > MAX_ATOM_CHARACTERS - sizeof(realloc) + 1) መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት በአሁኑ ጊዜ, ከፍተኛው ትክክለኛነት ርዝመት 248 characters ነው. ከፍተኛው ትክክለኛነት መጻፍ ጊዜ, overflow አይሆንም. Similar warnings: V557 Array overrun ሊሆን ይችላል. የ 'i' ቁጥጥር ዋጋ 40. ycf_lexer.c 493 ሊሆን ይችላል V557 Array overrun is possible. The value of 'n' index could reach 16. erl_call.c 1663 Fragment N9 I'd like to introduce this example in an unusual way. First, let's look at this function: static const char* event_state_flag_to_str(EventStateFlags f, const char *buff, int len) { switch ((int)f) { case ERTS_EV_FLAG_CLEAR: return "CLEAR"; case ERTS_EV_FLAG_USED: return "USED"; /* other cases that return string literals */ default: snprintf((char *)buff, len, "ERROR(%d)", f); return buff; } } Enter fullscreen mode Exit fullscreen mode The function converts the enumeration to a string. For non- ፎቶዎች, ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ (C11 የ C23 አግኙን event_state_flag_to_str EventStateFlags default switch const 7.3 እና 6.6 6.7.4.1.7 ይህ በእርግጥ ነው? የእርስዎን መግቢያ ገጽ ይመልከቱ: static ERTS_INLINE void print_flags(erts_dsprintf_buf_t *dsbufp, EventStateFlags f) { const char buff[64]; if (f & ERTS_EV_FLAG_WANT_ERROR) { erts_dsprintf(dsbufp, "WANTERR|"); f &= ~ERTS_EV_FLAG_WANT_ERROR; } erts_dsprintf(dsbufp, "%s", event_state_flag_to_str(f, buff, sizeof(buff))); } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት The function in question is called by passing the constant buffer as the second argument. Unfortunately, we get undefined behavior. We found this error using the analyzer, but it issued a confusing warning: የ "buff" አጠቃቀም የ "event_state_flag_to_str" ተግባር ውስጥ ሁለተኛው እውነተኛ አጠቃቀም ይመልከቱ. የ V614 erl_check_io.c 2833 Let's fix the code by removing the constness from the buffer: static ERTS_INLINE void print_flags(erts_dsprintf_buf_t *dsbufp, EventStateFlags f) { char buff[64]; if (f & ERTS_EV_FLAG_WANT_ERROR) { erts_dsprintf(dsbufp, "WANTERR|"); f &= ~ERTS_EV_FLAG_WANT_ERROR; } erts_dsprintf(dsbufp, "%s", event_state_flag_to_str(f, buff, sizeof(buff))); } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት በተጨማሪም እባክዎን ይቀየዳል function signature, so the parameter can take a ይህ ተግባር ወደ አግኝቷል buffer መተግበሪያ ይችላሉ ያውቃል. ይህ ተግባር በኮምፒውተር ፋይሎች ውስጥ ብቻ ይገኛል እና በኮምፒውተር አግኝቷል. . event_state_flag_to_str char* static Fragment N10 የ PVS-Studio ትዕዛዞች: The uninitialized class member 'a' is used when initializing the base class 'BeamAssemblerCommon'. አማርኛ1050 beam_asm.hpp 87 class BeamAssemblerCommon : public ErrorHandler { BaseAssembler &assembler; protected: BeamAssemblerCommon(BaseAssembler &assembler); .... }; struct BeamAssembler : public BeamAssemblerCommon { BeamAssembler() : BeamAssemblerCommon(a) { /* .... */ } protected: a64::Assembler a; .... }; መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት This fragment contains a small class hierarchy where a reference to a non-static member function of the derived class, የፕላስቲክ ኮምፒውተር የፕላስቲክ ኮምፒውተር የፕላስቲክ ኮምፒውተር, . This operation won't cause any issues as long as the base class constructor doesn't interact with this member function. Why? The lifetime of an object from a derived class starts when the required initializer in the initialization list has been executed or, if there's no initializer, when the control flow enters the constructor body. BeamAssembler BeamAssemblerCommon እነዚህን መረጃን በመውሰድ, ወደ base class constructor ይመልከቱ. ይህ የኦሪጂናል አጠቃቀም አይችሉም, አዎ? BeamAssemblerCommon::BeamAssemblerCommon(BaseAssembler &assembler_) : assembler(assembler_), .... { .... #ifdef DEBUG assembler.addDiagnosticOptions(DiagnosticOptions::kValidateAssembler); #endif assembler.addEncodingOptions(EncodingOptions::kOptimizeForSize | EncodingOptions::kOptimizedAlign); .... } Enter fullscreen mode Exit fullscreen mode Nope :) አንድ non-static member ተግባር አንድ ሕይወት መጀመር አይደለም ጊዜ አንድ ማመልከቻ በመጠቀም አንድ የተመሠረተ ክፍል ንጥረ ነገር ላይ ይደውሉ. በተጨማሪም, እኛ ያልተመሠረተ እንቅስቃሴ ያገኛሉ. I also find it difficult to suggest a fix in this case, so I'll leave that to the developers. Memory errors Fragment N11 የ PVS-Studio ትዕዛዞች: የ "ቀላቀ" ተግባር ሁለት ጊዜ ተመሳሳይ የሙዚቃ ቦታ ለማስተካከል ይደውሉ. የ 5886 አግኝቷል 668 int main(int argc, char *argv[]) { int fd; char *p = NULL; ei_cnode ec; .... int i = 0; ei_x_buff reply; .... if (ei_rpc(&ec, fd, "c", "c", p, i, &reply) < 0) { free(p); ei_x_free(&reply); fprintf(stderr,"erl_call: can't compile file %s\n", fname); } free(p); /* FIXME complete this code FIXME print out error message as term if (!erl_match(erl_format("{ok,_}"), reply)) { fprintf(stderr,"erl_call: compiler errors\n"); } */ ei_x_free(&reply); .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት የእርስዎን ስም ላይ ያውቃል, የ A አንድ አነስተኛ ቁጥር ይቀላቀሉ. አንድ አነስተኛ ቁጥር ይቀላቀሉ. አንድ አነስተኛ ቁጥር ይቀላቀሉ. አንድ አነስተኛ ቁጥር ይቀላቀሉ. አንድ አነስተኛ ቁጥር ይቀላቀሉ. pointer is passed to the function and an error is logged to ቀጣይ, የኮምፒውተር ውጤት ከሆኑ, የኮምፒውተር ወደ again. Memory is released twice. ei_rpc የመላኪያ ሂደት p free stderr free The fix is pretty simple: either stop the program execution or set the freed pointers to zero. To fix the issue, I would interrupt execution here: if (ei_rpc(&ec, fd, "c", "c", p, i, &reply) < 0) { free(p); ei_x_free(&reply); fprintf(stderr,"erl_call: can't compile file %s\n", fname); return 1; } free(p); መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት Fragment N12 የ PVS-Studio ትዕዛዞች: The 'pkey' pointer was utilized before it was verified against nullptr. Check lines: 581, 582. የ 595 የኮምፒዩተር 581 static int get_pkey_public_key(ErlNifEnv *env, const ERL_NIF_TERM argv[], int algorithm_arg_num, int key_arg_num, EVP_PKEY **pkey, ERL_NIF_TERM *err_return) { .... if (enif_is_map(env, argv[key_arg_num])) { password = get_key_password(env, argv[key_arg_num]); *pkey = ENGINE_load_public_key(e, id, NULL, password); if (!pkey) assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "Couldn't get public key from engine")); } /* other branches */ ret = 1; done: .... return ret; err: .... *pkey = NULL; ret = 0; goto done; } Enter fullscreen mode Exit fullscreen mode We have quite an interesting code fragment here. The attempts to extract the public key to the fifth out parameter. It returns ስኬታማ እና አንዳንዱ ጊዜ, የግል መውሰድ ደግሞ ይቀበላሉ. ተግባር 1 0 የቴክኒካዊ መሳሪያዎች ውስጥ, አንድ ክፍሎች ውስጥ, የ pointer is dereferenced before being checked for validity. However, other branches don't seem to have such checks. We can conclude that the function has the following contract: "The parameter must never be equal to a ." We can confirm this by searching for calls to this function in the same file ([ በ [ ] በ [ ] ])—the variable address is passed everywhere. pkey pkey nullptr 1 2 3 በእርግጥ, የ developers የሚፈልጉት ነው በይለፍ ውጤት ለመርዳት መጫወት ወደ ጫፍ ነገር ግን, እነሱም አንድ አስትራክን በፊት መጫወት መውሰድ ነበር. operator. A modern compiler will ይህ መቆጣጠሪያ በይፋ ላይ ይፈጥራል. ውጤት በይፋ ላይ ይፈጥራል. , እና አንድ null ትዕዛዞች በ 5 ኛው ፓራሚተር ውስጥ ይጀምራል. ENGINE_load_public_key err ! የተሻለ ይሰጣል 1 የሽያጭ ማረጋገጫ ይመልከቱ: .... *pkey = ENGINE_load_public_key(e, id, NULL, password); if (!*pkey) assign_goto(*err_return, err, EXCP_BADARG_N(env, key_arg_num, "Couldn't get public key from engine")); .... Enter fullscreen mode Exit fullscreen mode Fragment N13 We often receive messages saying that the analyzer warns about passing a null pointer to መጨረሻም, መደበኛ ሁኔታ ምንም አስደናቂ ነገር ሊሆን አይችልም. እኛም ደግሞ አንድ አግኝተዋል about a similar case. free ጽሑፍ አዎ, ይህ ጥሩ ነው, ነገር ግን እንደዚህ ኮድ ምናልባት ከባድ ነው. ይህ ኮድ ውስጥ አንድ አስደናቂ የሙዚቃ ውሂብ ማግኘት ይረዳል. እሱን ይመልከቱ. The PVS-Studio warning: The null pointer is passed into 'free' function. Inspect the first argument. የ 575 erl_misc_utils.c 264 void erts_cpu_info_destroy(erts_cpu_info_t *cpuinfo) { if (cpuinfo) { cpuinfo->configured = 0; cpuinfo->online = 0; cpuinfo->available = 0; #ifdef HAVE_PSET_INFO if (cpuinfo->cpuids) free(cpuinfo->cpuids); #endif cpuinfo->topology_size = 0; if (cpuinfo->topology) { cpuinfo->topology = NULL; free(cpuinfo->topology); } free(cpuinfo); } } Enter fullscreen mode Exit fullscreen mode In this fragment, the pointer is explicitly set to before calling . As a result, the memory-freeing operation is pointless, since ይህ የሙዚቃ መውሰድ ያደርጋል, ምክንያቱም የመጀመሪያው የሙዚቃ መውሰድ pointed to is never freed. cpuinfo->topology NULL free free(NULL) cpuinfo->topology We can fix the issue by swapping the operations: if (cpuinfo->topology) { free(cpuinfo->topology); cpuinfo->topology = NULL; } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት Fragment N14 የ PVS-Studio ትዕዛዞች: The function was exited without releasing the 'entry' pointer. A memory leak is possible. የ 773 wxe_gl.cpp 88 typedef struct _wxe_glc { wxGLCanvas *canvas; wxGLContext *context; } wxe_glc; void setActiveGL(wxeMemEnv *memenv, ErlNifPid caller, wxGLCanvas *canvas, wxGLContext *context) { ErlNifUInt64 callId = wxe_make_hash(memenv->tmp_env, &caller); wxe_glc * entry = glc[callId]; gl_active_index = callId; gl_active_pid = caller; if (!entry) { entry = (wxe_glc *) malloc(sizeof(wxe_glc)); entry->canvas = NULL; entry->context = NULL; } if (entry->canvas == canvas && entry->context == context) return; entry->canvas = canvas; entry->context = context; glc[gl_active_index] = entry; .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት ውስጥ function, the ይህ መሳሪያ በ "Null Pointer" ነው, ይህ መሳሪያ በ "Null Pointer" ይጠቀማል. . Next, the pointers are compared. If it evaluates to , an early return will occur. However, if dynamic allocation occurred, the memory won't be released. setActiveGL entry malloc true አዎ, ይህ ሁኔታ በጣም ዝቅተኛ ነው እና ሊሆን አይችልም, ምክንያቱም ይህ ሙሉ ተከታታይ ሁኔታዎችን ያስፈልጋል: ይህ ተግባር ይደውሉ ጊዜ, የ 3 ኛ እና 4 ኛ አግኝተሮች እንደ 0 አግኝተዋል. አግኝቷል ፡፡ አግኝቷል ፡፡ ነገር ግን, እኛ የክፍያ ማንኛውም ጊዜ ይሆናል ለማረጋገጥ ይችላሉ, የቴክኒካዊ አይደለም. ወደ ኮድ ትንሽ ማሻሻል ይሆናል: ErlNifUInt64 callId = wxe_make_hash(memenv->tmp_env, &caller); wxe_glc * entry = glc[callId]; gl_active_index = callId; gl_active_pid = caller; bool new_alloc = false; if (!entry) { entry = (wxe_glc *) malloc(sizeof(wxe_glc)); entry->canvas = NULL; entry->context = NULL; new_alloc = true; } if (entry->canvas == canvas && entry->context == context) { if (new_alloc) { free(entry); } return; } .... Enter fullscreen mode Exit fullscreen mode ተመሳሳይ ጥያቄዎች: V773 The function was exited without closing the file referenced by the 'f' handle. A resource leak is possible. erl_poll.c 2423 V773 The 'erl_errno_p' pointer was assigned values twice without releasing the memory. A memory leak is possible. ei_pthreads.c 195 V773 The function was exited without closing the file referenced by the 'fp' handle. A resource leak is possible. cpu_sup.c 433 V773 The exception was thrown without releasing the 'data' pointer. A memory leak is possible. wxe_wrapper_4.cpp 1300 V773 በ "Data" ትዕዛዞች ማውረድ አይችልም. የሙዚቃ መውሰድ ሊሆን ይችላል. wxe_wrapper_4.cpp 1330 V773 በ "Data" ትዕዛዞች ማውረድ አይችልም. የሙዚቃ መውሰድ ሊሆን ይችላል. wxe_wrapper_4.cpp 1663 V773 በ "Data" ትዕዛዞች ማውረድ አይችልም. የሙዚቃ መውሰድ ሊሆን ይችላል. wxe_wrapper_4.cpp 1690 V773 The exception was thrown without releasing the 'data' pointer. A memory leak is possible. wxe_wrapper_4.cpp 1715 V773 The exception was thrown without releasing the 'alpha' pointer. A memory leak is possible. wxe_wrapper_4.cpp 1718 V773 The exception was thrown without releasing the 'data' pointer. A memory leak is possible. wxe_wrapper_4.cpp 1747 V773 በ "alpha" ትዕዛዞች ማውረድ አይችልም. የሙዚቃ ትዕዛዞች ሊሆን ይችላል. wxe_wrapper_4.cpp 1750 V773 The exception was thrown without releasing the 'alpha' pointer. A memory leak is possible. wxe_wrapper_4.cpp 2680 V773 በ "Data" ትዕዛዞች ማውረድ አይችልም. የሙዚቃ መውሰድ ሊሆን ይችላል. wxe_wrapper_4.cpp 2715 V773 በ "Data" ትዕዛዞች ማውረድ አይችልም. የሙዚቃ መውሰድ ሊሆን ይችላል. wxe_wrapper_4.cpp 2733 Fragment N15 የ PVS-Studio ትዕዛዞች: realloc() possible leak: when realloc() fails in allocating memory, original pointer 'outbuf_base' is lost. Consider assigning realloc() to a temporary pointer. V701 አግኝቷል 1324 static void outbuf_append(const char* buf, int n) { .... /* * Allocate a larger buffer if we still cannot fit the data. */ if (outbuf_base+outbuf_total < outbuf_in+n) { int size = outbuf_in - outbuf_out; outbuf_total = size+n; outbuf_base = realloc(outbuf_base, outbuf_total); outbuf_out = outbuf_base; outbuf_in = outbuf_base + size; } .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት There's a tricky pattern with the function, where the original pointer is immediately overwritten by the return value. Let's get to the bottom of this. realloc የፋንክሪት የመጀመሪያው ስሜት እና የመጀመሪያው ስሜት ሁለተኛው ስሜት ነው. የፋንክሪት ስኬታማ ነው ከሆነ, የፋንክሪት አዲስ የተመሠረተ የሙዚቃ ወደ አንድ ሻጋታ ይውላሉ. , even if only a simple memory block expansion has occurred. If the operation fails, the function returns a . The memory passed as the first argument remains . invalid null pointer untouched ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ ከባድ እኛም አንድ የገንዘብ መግዛት ይሆናል. realloc Let's fix the code as follows: static void outbuf_append(const char* buf, int n) { .... /* * Allocate a larger buffer if we still cannot fit the data. */ if (outbuf_base + outbuf_total < outbuf_in + n) { int size = outbuf_in - outbuf_out; outbuf_total = size+n; char *tmp = realloc(outbuf_base, outbuf_total); if (!tmp) { /* somehow handle this scenario * the `outbuf_base` buffer here is still valid */ } outbuf_base = tmp; outbuf_out = outbuf_base; outbuf_in = outbuf_base + size; } .... } Enter fullscreen mode Exit fullscreen mode በፈጣን ምሳሌ ውስጥ, እኛ ደግሞ በዚያ ሁኔታን ለመገናኘት አለብዎት returns the null pointer. This may be an exception throwing, an attempt to allocate a buffer of a different size, and so on. realloc Fragment N16 The PVS-Studio warning: The 'end' pointer in the 'end - mm->sua.top' expression equals nullptr. The resulting value is senseless and it should not be used. የ 769 አግኝቷል 2411 static void add_free_desc_area(ErtsMemMapper* mm, char *start, char *end) { ERTS_MMAP_ASSERT(end == (void *) 0 || end > start); if (sizeof(ErtsFreeSegDesc) <= ((UWord) end) - ((UWord) start)) { .... } .... } void erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) { .... if (end == (void *) 0) { /* * Very unlikely, but we need a guarantee * that `mm->sua.top` always will * compare as larger than all segment pointers * into the super carrier... */ mm->sua.top -= ERTS_PAGEALIGNED_SIZE; mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE; #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE)) #endif add_free_desc_area(mm, mm->sua.top, end); mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc); } .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር የኮምፒውተር ወደ ኮድ አግኝቷል, ይህም is indeed a null pointer. The data member is also a pointer, but we don't know its value yet. According to the C standard, the behavior of pointers that differ is defined only when they both point to elements of the same array (C11 የ C23 በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ "Null Pointer" በ " end mm->sua.top 6.5.6.8 6.5.7.10 እኔ ምን ይሆናል በ አጠቃቀም add_free_desc_area static void add_free_desc_area(ErtsMemMapper* mm, char *start, char *end) { ERTS_MMAP_ASSERT(end == (void *) 0 || end > start); if (sizeof(ErtsFreeSegDesc) <= ((UWord) end) - ((UWord) start)) { .... } መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት መኖሪያ ቤት በዚያ ላይ ደግሞ ትኩረት ይጠቀማል, ነገር ግን አንድ ልዩነት ጋር: የመጀመሪያው, ትኩረት ወደ ቁጥር ይቀበሉ. ይህ implementation-defined እንቅስቃሴ ነው (C11) ; C23 ), ይህም በጣም ጥሩ ነው. 6.3.2 እና 3.6 6.3.2 እና 3.6 በእርግጥ, እኔ አንድ ትክክለኛ መተግበሪያ መተግበሪያ መተግበሪያ መተግበሪያ መተግበሪያ መተግበሪያ ይፈልጋሉ. Conclusion Well, that concludes the article. As expected with any large project that has had a long and eventful history, we found various errors. They're also part of the project, though. It's important to keep in mind that finding these kinds of issues in a code base that's over three decades old isn't a sign of weakness. Rather, it's proof of the code's incredible scale and longevity. There are millions of code lines written by hundreds of developers, and the system has been running for years without rebooting. This project deserves the deepest respect, not because it's perfect, but because it has stood the test of time and proven its real power. Thanks to constantly evolving modern technologies, developers have powerful tools at their disposal to improve code reliability. For example, static analyzers help identify hidden errors and potential vulnerabilities. I invite you to see it for yourself :) You can get a trial version of PVS-Studio analyzer . here