Msimbo kwa baadhi ya modules ya Erlang / OTP ni zamani kuliko watengenezaji wengi wa kisasa. Faili hizi ni patriarchs halisi ya digital. Kwa miongo kadhaa, wamekuwa na uhakika kwamba shughuli za benki, mitandao ya simu, na mifumo ya ujumbe ni kazi vizuri. Tumeamua kuchunguza chini ya kichwa cha lugha hii ya muda mrefu kuona nini hasa iko nyuma ya mstari ambao mamilioni ya watumiaji kutegemea leo. Katika makala hii, hebu tuone nini tulipata. Maelezo ya Kabla ya kuendelea na kujadili tahadhari, ningependa kusema maneno machache kuhusu mradi huo. Safari ya Erlang ilianza mwaka 1986 katika moja ya majaribio ya Ericsson. Joe Armstrong alikuwa na majaribio na Prolog wakati akijaribu kujenga mpango wa simu. ni lugha ya mipangilio iliyoundwa kwa mifumo ya kukabiliana na makosa ambapo kushindwa ni isiyokubalika. Fikiria kifungo cha simu ambapo wito uliopoteza unachukuliwa kama dharura, au shughuli ya benki ambayo inapaswa kupita bila kujali ni nini. Hizi ni kazi ambazo hutumiwa kwa. Inakuwezesha mfumo kugawana katika mamilioni ya mchakato wa kujitenga, huru, hivyo wakati mmoja akashindwa, wengine huendelea kuendesha bila kupoteza shinikizo. Maana ya Erlang goes hand in hand with the , mfumo ambao hutoa zana yenye nguvu, mifano, na tabia kwa ajili ya kujenga maombi ya kusambazwa, ya kukabiliana na makosa. Kitabu hiki cha zana cha ndani kinawezesha mifumo yaliyoandikwa nayo kuendesha bila kuacha kwa miaka mingi. Mfumo wa Telecom Open (OTP) Tumeanzisha mradi huu kwa kufuata Tumefanya uchunguzi kwenye Mifumo ya kutumia na ya tunakabiliwa na matatizo yoyote na ujenzi wa mradi, hivyo kudos kwa watengenezaji. Maelekezo ya maint-28 PvS-Studio ya uchambuzi Kodi ya Visual Studio Makala hii inajumuisha tahadhari za kiwango cha juu na cha kati tu. Hiyo ni mwisho wa ufunguzi wetu. Sasa, hebu tuendelee na tahadhari. Sisi kuweka yao katika makundi matatu: makosa ya mantiki; makosa ya kimaadili; Makosa ya kumbukumbu. Makosa ya mantiki Fragment N1 Maoni ya Studio ya PvS: Kuna sub-maonyesho sawa '(!esock_is_integer((env), (argv[3])))' upande wa kushoto na wa kulia wa operesheni ya 'annoo. wa 501 Mstari wa chini: 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]); } .... } Kuingia mode full screen Kuondoka mode full screen Katika msimbo, watengenezaji walitaka kubadilisha maneno ya tatu na ya nne kwa aina za 64-bit (yaani, maneno yaliyoandikwa na yasiyoandikwa). kazi inarejea mapema ikiwa moja ya maneno sio namba. Msimbo wa kudumu: .... if ((! IS_INTEGER(env, argv[2])) || (! IS_INTEGER(env, argv[3]))) return enif_make_badarg(env); .... Kuingia mode full screen Kuondoka mode full screen Fragment N2 Maoni ya Studio ya PvS: Kuna sub-maonyesho sawa 'n->type == ycf_node_type_on_save_yield_state_code' upande wa kushoto na wa kulia wa operesheni ya 'annoo. wa 501 Kifungu cha Kifungu cha 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); } .... } Kuingia mode full screen Kuondoka mode full screen Hapa chini unaweza kusoma mifano minane ya baadhi ya nukuu za kukumbukwa na kwa hakika za kushangaza zilizowahi kutolewa na wanasiasa wa Malysia katika miaka ya hivi karibuni: 1. kazi, ambayo inashughulikia nodes ya mti mara kwa mara na hutafuta maeneo ya kuonekana na mabadiliko ya ndani. Kisha huita kazi ya kuunganisha jina la mabadiliko kwa kila moja ya maeneo haya. branch, the Mkataba huo umekamilika baada ya wa Operesheni. uniqify_local_vars_in_node else if n->type == ycf_node_type_on_save_yield_state_code || Kwa bahati mbaya, sikuwa na ufumbuzi sahihi kwa kifungu hiki. Kutokana na mstari wa kulinganisha, thamani za orodha ifuatayo zinahesabiwa kwa utaratibu ambao wanadaiwa: 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; Kuingia mode full screen Kuondoka mode full screen Hata hivyo, wa Kiwango cha kudhibitiwa katika kipindi cha awali Hivyo, nadhani kuna udhibiti wa duplicate hapa baada ya yote, na tunaweza kufuta. ycf_node_type_on_restore_yield_state_code else if Fragment N3 Maoni ya Studio ya PvS: Possible typo in the spelling of a pre-defined macro name. The ' ‘Macro ni sawa na’ “Kwa Mji wa 1040 WIN_32 WIN32 Mkataba wa Mkataba wa Mkataba wa Mkataba wa Mkataba wa Mkataba wa Mkataba wa Mkataba 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); } Kuingia mode full screen Kuondoka mode full screen This is a rather unusual case of checking whether a project compiles under Windows. Erlang has other checks on Windows ([ Kwa mfano, [ Kwa mfano, [ ], etc.), but this one is unique. This looks very much like a simple typo (most likely, it should be ). 1 2 3 __WIN32__ But why is this the case? Out of curiosity, I searched for Hebu tuone jinsi gani inaweza kuomba kwa scholarship M.Sc katika Korea: "__WIN_32__" Kama unaweza kuona, kuna karibu hakuna matokeo. Ni funny kwamba moja ya viungo inaongoza kwa faili sisi ni kuangalia juu Nilichukua kupitia mradi wa kupata hakuna ufafanuzi wa makro sawa. Pia nilifikiri inaweza kuwa imewekwa katika compiler fulani, kama MinGW. Kwa bahati mbaya, Baada ya kujaribu kuelewa madhumuni ya makro, nadhani kwamba msimbo huu ni makosa. Vyanzo vya Debian Majaribio ya Kama ufumbuzi, ninapendekeza kutumia cheque niliyopata hapo awali katika sehemu zingine za mradi: static int tcp_send_error(tcp_descriptor* desc, int err) { .... #if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_) if (err == ECONNABORTED) err = ECONNRESET; #endif .... } Kuingia mode full screen Kuondoka mode full screen Fragment N4 The PVS-Studio warning: Logic ya uendeshaji wa msimbo haina kukubalika na muundo wake. kauli ya pili daima itafanywa. wa 640 Mifano ya C 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"); .... } Kuingia mode full screen Kuondoka mode full screen Hapa ni mfano wa kuvutia wa makosa ya kawaida ya coding ambayo inaweza kuwa vigumu kutambua baadaye. Muundo wa: PUSH2 #define QUOTE(s) s #define PUSH(s) eargv[eargc++] = QUOTE(s) #define PUSH2(s, t) PUSH(s); PUSH(t) Kuingia mode full screen Kuondoka mode full screen Uh-huh, ni makro ambayo inaongezeka katika miundo miwili. Hebu kupanua msimbo kama preprocessor ingekuwa: 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 As a result, if the check evaluates to , tu utaratibu wa kwanza utafanywa kwa masharti, wakati wa pili daima kutokea baada ya shamba. Hii sio kitu ambacho watengenezaji walitaka kufikia. false Kuandika na kutumia macros Suluhisho kamili litakuwa kuondokana nao, lakini hebu tufanye ufumbuzi mdogo. watengenezaji mara nyingi hutumia kujenga wakati wanahitaji kuunganisha vitendo kadhaa katika moja ndani ya makro. Kuvutia kwa Bugs do {...} while (0) #define PUSH2(s, t) do { PUSH(s); PUSH(t); } while (0) Kuingia mode full screen Kuondoka mode full screen Fragment N5 Maoni ya Studio ya PvS: Hali ya 'i >= 0' ya loop daima ni kweli. wa 654 wxe_return.cpp ya 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; } Kuingia mode full screen Kuondoka mode full screen Angalia moja kwa moja kwenye Kuanza kucheza katika TopSlotSite , kufuata 3 hatua rahisi kujiandikisha. Mabadiliko ya induction ni ya Kiwango cha thamani kwa kiwango cha Baada ya hapo, mwanga utakuwa usio na mwisho. Baada ya mwanga iterates Mabadiliko yanakuwa sawa na kutokana na kupungua, na mzunguko unaendelea. for i unsigned int [0; UINT_MAX] i == 0 UINT_MAX Hebu tufanye msimbo kama ifuatavyo: for (int64_t i = arr.GetCount() - 1; i >= 0; i--) { .... } Kuingia mode full screen Kuondoka mode full screen Maoni ya tahadhari: V654 Hali 'i >= 0' ya loop ni kweli daima. wxe_return.cpp 248 V654 Hali 'i >= 0' ya loop ni kweli daima. wxe_return.cpp 260 Fragment N6 Maoni ya Studio ya PvS: Kuanzisha mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi. wa 517 erl_bif_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 .... } } Kuingia mode full screen Kuondoka mode full screen Mchambuzi umegundua mashirika kadhaa na udhibiti sawa katika kazi ambayo ina idadi kubwa ya Ufafanuzi - karibu Kila mmoja wao, hata hivyo, ina mantiki tofauti: na ya Kutokana na idadi ya mashirika na mstari wa 150 kati ya duplicates, haishangazi kwamba hii inaweza kutokea. uchambuzi wa static husaidia kuzuia matukio kama hayo. if-else if Mstari wa 800 Uchunguzi wa kwanza Uchunguzi wa pili Fragment N7 Maoni ya Studio ya PvS: A part of conditional expression is always false: (arity == 0). wa 560 Kifungu cha 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); } .... } Kuingia mode full screen Kuondoka mode full screen Hapa, tuna mfano unaohusisha kimantiki. uchambuzi anatuambia kwamba Maelezo ya chini daima ni sahihi.Kwa nini? 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 . We can also learn the following: false ix >= 1 ix <= arity Kuingia mode full screen Kuondoka mode full screen Tutaandika tena maneno haya kwa kiwango cha kimantiki: 1 <= ix <= arity Kuingia mode full screen Kuondoka mode full screen Baada ya hapo, tunachukua a ambayo inasema kuwa . So, Hatuwezi kamwe kuwa 0 katika hali hii. Sheria ya uhusiano wa mpito arity >= 1 arity Nitaacha pia kurekebisha msimbo huu kwa watengenezaji, kwa sababu mapendekezo yangu pekee ni kuondoa hali. Makosa ya kritiki Fragment N8 Ripoti ya PvS-Studio inasema: Array overrun is possible. The value of 'prefix_len' index could reach 262. wa 557 Msisemi Shaykh Rabiy ́ ni Imaam wa Jarh wat-Ta ́diyl. Array overrun is possible. The value of 'prefix_len' index could reach 262. wa 557 Msisemi Shaykh Rabiy ́ ni Imaam wa Jarh wat-Ta ́diyl. Array overrun is possible. The value of 'prefix_len' index could reach 262. V557 Msisemi Shaykh Rabiy ́ ni Imaam wa Jarh wat-Ta ́diyl. #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 Tuna kazi ambayo hutoa majina kwa ajili ya utoaji na deallocation kazi kulingana na prefix iliyotolewa kwake. buffer ya 255-byte hutumiwa kuzalisha yao. Kutokana na maudhui ya kazi, hakuna terminator ya null katika buffer iliyotengenezwa, kwa sababu inapita ukubwa wa jumla wa mstari uliotengenezwa. am_atom_put Kitu cha kwanza ambacho watengenezaji walifanya ni kuhesabu ukubwa wa prefix.Kisha, walipunguza ukubwa wa prefix: ikiwa ni zaidi ya jumla ya Na kiwango cha muda wa string (the longest postfix), the kazi ya kuitwa. MAX_ATOM_CHARACTERS realloc noreturn Next, they copied the prefix to the buffer, added the postfix, and passed the string to the function. The devs performed all these actions sequentially for all postfixes: , na ya . am_atom_put alloc realloc free Unaweza kutambua upatikanaji? Utaratibu wa kurudi mapema ulifanywa vibaya. Utaratibu wa buffer unapaswa kuwa na nafasi ya kutosha kuandika postfix ya muda mrefu, ambayo ni wahusika saba. Ikiwa mstari wa awali wa awali ni kati ya wahusika 256 na 262, kurudi mapema haina kutokea, hivyo function overflows the buffer. This operation has undefined behavior. sys_memcpy Kutokana na ukweli kwamba function works with strings no longer than Tutaweza kurekebisha msimbo kama ifuatavyo: am_atom_put MAX_ATOM_CHARACTERS if (prefix_len > MAX_ATOM_CHARACTERS - sizeof(realloc) + 1) Kuingia mode full screen Kuondoka mode full screen Sasa, kiwango cha juu cha muda wa prefix ni wahusika 248. Wakati wa kuandika kiwango cha juu cha postfix, upungufu hautafanyika. Similar warnings: V557 Array overrun is possible. The value of 'i' index could reach 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 ya function converts the Kuanza kucheza katika TopSlotSite , kufuata kwa ajili ya. branches, returns a string literal. Otherwise, the function creates a string in the provided buffer and returns a pointer to it. Note how exactly it does this: the buffer forcibly discards constness. According to the C standard, this is allowed only if the source buffer hasn't been declared as (Kifungu cha 11 wa C23 ). event_state_flag_to_str EventStateFlags default switch const 6.7.3.6 7.1 ya 1.7 Je, hii ni kweli? hebu tuangalie hatua ya maombi: 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))); } Enter fullscreen mode Exit fullscreen mode Function katika suala ni kuitwa kwa kupita buffer ya kudumu kama hoja ya pili. Kwa bahati mbaya, sisi kupata tabia isiyojulikana. Tulipata makosa hii kwa kutumia analyzer, lakini alitoa tahadhari ya kuchanganyikiwa: Uninitialized buffer 'buff' kutumika. Angalia kuangalia argument ya pili halisi ya 'event_state_flag_to_str' kazi. wa 614 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))); } Kuingia mode full screen Kuondoka mode full screen Pia tunapendekeza mabadiliko ya function signature, so the parameter can take a pointer. This makes it clear that the function can modify the buffer passed to it. We won't be breaking much code in the process since the function exists only in the compiled file and is marked as . event_state_flag_to_str char* static Fragment N10 Maoni ya Studio ya PvS: Mshiriki wa darasa isiyojulikana 'a' hutumiwa wakati wa kuanzisha darasa la msingi 'BeamAssemblerCommon'. V1050 Msisemi Shaykh Rabiy ́ ni Imaam wa Jarh wat-Ta ́diyl. class BeamAssemblerCommon : public ErrorHandler { BaseAssembler &assembler; protected: BeamAssemblerCommon(BaseAssembler &assembler); .... }; struct BeamAssembler : public BeamAssemblerCommon { BeamAssembler() : BeamAssemblerCommon(a) { /* .... */ } protected: a64::Assembler a; .... }; Enter fullscreen mode Exit fullscreen mode This fragment contains a small class hierarchy where a reference to a non-static member function of the derived class, , is passed to the constructor of the base 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 Kwa habari hii katika akili, hebu tuangalie muumba wa darasa la msingi. Haifanyi matumizi ya kitu hiki, sivyo? 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 :) A non-static member function is called on an object of a derived class via a reference when its lifetime has not yet begun. Again, we get undefined behavior. I also find it difficult to suggest a fix in this case, so I'll leave that to the developers. Makosa ya kumbukumbu Fragment N11 The PVS-Studio warning: "Free" kazi huitwa mara mbili kwa usambazaji wa nafasi hiyo ya kumbukumbu. Maoni ya 586 Msisemi Shaykh Rabiy ́ ni Imaam wa Jarh wat-Ta ́diyl. 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); .... } Enter fullscreen mode Exit fullscreen mode Kwa mujibu wa jina, ya a . A negative number is returned if the operation fails. If the remote call fails, some resources are cleaned up. The Pointer inapatikana kwa kazi na makosa yameandikwa kwa Kisha, bila kujali matokeo ya wito wa kazi, pointer inapelekwa kwenye Kumbukumbu inachukuliwa mara mbili. ei_rpc Utaratibu wa Remote Call 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); Kuingia mode full screen Kuondoka mode full screen Fragment N12 The PVS-Studio warning: Kiungo cha 'pkey' kilitumiwa kabla ya kuthibitishwa dhidi ya nullptr. viwambo vya ukaguzi: 581, 582. wa 595 Kifungu cha 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 if successful and if not. In the latter case, the public key is zeroed as well. Kazi ya 1 0 Mchambuzi anaonya kwamba, katika moja ya majengo ya kazi, pointer ni dereferenced kabla ya kuangalia kwa ajili ya uhalali. Hata hivyo, mashirika mengine haionekani kuwa na udhibiti huo. Tunaweza kuhitimisha kwamba kazi ina mkataba wafuatayo: "The Utaratibu kamwe hauwezi kuwa sawa na a Tunaweza kuthibitisha hili kwa kutafuta wito kwa kazi hii katika faili moja ([ ], [ Kwa mfano, [ ])—the variable address is passed everywhere. pkey pkey nullptr 1 2 3 Kwa kweli, watengenezaji walitaka kushughulikia matokeo yasiyo sahihi ya function by jumping to the mark. However, they made a typo by forgetting to put an asterisk before the Kuanzisha mlinzi juu ya msalaba wa mwisho. at this check on release. As a result, the function will return , na mwongozo wa null utaishia katika kiwango cha tano. ENGINE_load_public_key err ! Ufanisi wa mbali 1 Hebu tufanye kazi ya kuangalia: .... *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")); .... Kuingia mode full screen Kuondoka mode full screen Fragment N13 We often receive messages saying that the analyzer warns about passing a null pointer to Baada ya yote, viwango vinasema kwamba hakuna kitu cha kutisha kitatokea. about a similar case. free article Ndiyo, hiyo ni nzuri, lakini msimbo kama huo unaweza kuwa na shaka. Hiyo ilisaidia kupata upungufu wa kumbukumbu wa kuvutia katika msimbo. Maoni ya Studio ya PvS: Kuanza kucheza katika TopSlotSite , kufuata 3 hatua rahisi kujiandikisha. V575 Mifano ya Mifano ya Mifano ya Mifano ya Mifano ya Mifano 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); } } Kuingia mode full screen Kuondoka mode full screen In this fragment, the pointer ni wazi kwa ajili ya Kabla ya kupiga simu Kwa hiyo, operesheni ya kuokoa kumbukumbu haina maana, kwa sababu Hii inasababisha kuenea kwa kumbukumbu kwa sababu block ya kumbukumbu ya awali ambayo pointed to is never freed. cpuinfo->topology NULL free free(NULL) cpuinfo->topology Tunaweza kurekebisha tatizo hili kwa kubadilisha operesheni: if (cpuinfo->topology) { free(cpuinfo->topology); cpuinfo->topology = NULL; } Enter fullscreen mode Exit fullscreen mode Fragment N14 The PVS-Studio warning: Function iliondoka bila kuachana na pointer ya 'ingiza'. upatikanaji wa kumbukumbu inawezekana. Maoni ya 773 Kifungu cha Kifungu cha Kifungu cha 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; .... } Enter fullscreen mode Exit fullscreen mode In the ya kazi, ya Picha hii ilipigwa tokea ktk veranda za moja ya vyumba vya Manyara Serena Lodge. Pamoja na hayo, viwango vya juu vya ubadilishaji vinavyokubaliwa, ikiwa ni pamoja na: , kurudi mapema itatokea. Hata hivyo, ikiwa usambazaji wa nguvu ulifanyika, kumbukumbu haitafunguliwa. setActiveGL entry malloc true Yes, such a situation is rather rare and unlikely to happen, because it requires a whole series of events: wakati kazi ni wito, zero pointers ni kupitishwa kama arguments tatu na nne. contains a null pointer. glc[callId] However, we can ensure that a leak will never occur, not even theoretically. Let's modify the code a little: 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; } .... Kuingia mode full screen Kuondoka mode full screen Maoni ya tahadhari: V773 Function iliondoka bila kufunga faili iliyotajwa na "f" kuendesha. upungufu wa rasilimali inawezekana. 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 Function iliondoka bila kufunga faili iliyotajwa na 'fp' handle. upungufu wa rasilimali inawezekana. 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 Upatikanaji uliondolewa bila kutolewa kwa pointer ya 'data'. upungufu wa kumbukumbu unaweza. wxe_wrapper_4.cpp 1330 V773 Upatikanaji uliondolewa bila kutolewa kwa pointer ya 'data'. upungufu wa kumbukumbu unaweza. wxe_wrapper_4.cpp 1663 V773 The exception was thrown without releasing the 'data' pointer. A memory leak is possible. 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 The exception was thrown without releasing the 'alpha' pointer. A memory leak is possible. wxe_wrapper_4.cpp 1750 V773 Hifadhi ilichukuliwa bila kutolewa kwa pointer ya 'alpha'. upungufu wa kumbukumbu ni uwezekano. wxe_wrapper_4.cpp 2680 V773 Upatikanaji uliondolewa bila kutolewa kwa pointer ya 'data'. upungufu wa kumbukumbu unaweza. wxe_wrapper_4.cpp 2715 V773 Upatikanaji uliondolewa bila kutolewa kwa pointer ya 'data'. upungufu wa kumbukumbu unaweza. wxe_wrapper_4.cpp 2733 Fragment N15 The PVS-Studio warning: realloc() possible leak: when realloc() fails in allocating memory, original pointer 'outbuf_base' is lost. Consider assigning realloc() to a temporary pointer. wa 701 Mkataba wa Mkataba wa Mkataba wa 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; } .... } Kuingia mode full screen Kuondoka mode full screen Kuna muundo wa tricky na function, where the original pointer is immediately overwritten by the return value. Let's get to the bottom of this. realloc The function takes the buffer for reallocation as its first argument and the new buffer size as its second argument. If the operation is successful, the function will return a pointer to the newly allocated memory. The memory passed as the first argument becomes , hata kama upanuzi rahisi wa mstari wa kumbukumbu umefanyika. Ikiwa operesheni haifanyi kazi, kazi kumbukumbu iliyopita kama hoja ya kwanza inabaki . invalid null pointer untouched Hivyo, kama sisi mara moja kuandika juu ya pointer ya awali na wito kwa , we will get a memory leak. 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 Katika mfano wa imara, tunahitaji pia kushughulikia hali ambapo 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 Maoni ya Studio ya PvS: Mwongozo wa 'mwisho' katika maneno 'mwisho - mm->sua.top' ni sawa na nullptr. thamani inayotokana ni isiyo na maana na haipaswi kutumika. V769 erl_mmap.c 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); } .... } Enter fullscreen mode Exit fullscreen mode Uchambuzi umegundua arithmetic ya anwani ya shaka.Tazama nini kinachotokea hapa. We enter the code branch where Huu ni mtazamo wa kutosha. Kwa mujibu wa kiwango cha C, tabia ya pointers ambazo ni tofauti zinafafanuliwa tu wakati wote wawili wanaonyesha vitu vya mstari mmoja (C11). kwa ajili ya C23 ). The null pointer doesn't point to any array element. So, the behavior of this operation is undefined, which isn't good. end mm->sua.top 5.6 ya 8 5.7 ya 10 I was wondering what happens in the ya kazi. 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)) { .... } Enter fullscreen mode Exit fullscreen mode Pointer subtraction pia hutumiwa hapa, lakini na tofauti: kwanza, pointers ni kubadilishwa kwa namba. ; C23 ambayo ni bora zaidi. 6.3.2 ya tatu 6.3.2 ya tatu Kwa bahati mbaya, siwezi kupendekeza ufumbuzi sahihi. Watengenezaji wanaweza kuangalia suala hili. Conclusion Naam, hiyo inamalizia makala. Kama ilivyotarajiwa na mradi wowote mkubwa ambao umekuwa na historia ndefu na yenye matukio, tuligundua makosa mbalimbali. Wao pia ni sehemu ya mradi, hata hivyo. Ni muhimu kukumbuka kwamba kupata masuala ya aina hii katika msingi wa msimbo ambao ni zaidi ya miongo mitatu sio ishara ya udhaifu. Badala yake, ni ushahidi wa kiwango cha ajabu cha msimbo na maisha ya muda mrefu. Kuna mamilioni ya mstari wa msimbo ulioandikwa na mamia ya watengenezaji, na mfumo umekuwa unaendesha kwa miaka bila reboot. Mradi huu unastahili heshima kubwa, si kwa sababu ni kamili, lakini kwa sababu imekabiliwa na mtihani wa wakati na kuthibitisha nguvu yake halisi. Shukrani kwa teknolojia za kisasa zinazoendelea, watengenezaji wana zana yenye nguvu katika uwezo wao wa kuboresha uaminifu wa msimbo. Kwa mfano, uchambuzi wa static husaidia kutambua makosa yaliyomo na makosa ya uwezekano. Nataka uone mwenyewe :) Unaweza kupata toleo la majaribio ya uchambuzi wa PVS-Studio . Hapa ya