PythonずRubyの実装の゚ラヌ密床を比范する

孊習を開始するプログラミング蚀語PythonたたはRuby どちらが良いですか DjangoたたはRuby on Railsですか このような質問は、䞖界䞭のITフォヌラムでよく芋られたす。 蚀語自䜓ではなく、それらのリファレンス実装であるCPythonずMRIを比范するこずを提案したす。 むンタヌプリタヌの゚ラヌPVS-Studioで怜出できる゚ラヌに぀いおは、この蚘事で説明したす。











はじめに



分析のために、最新の゜ヌスは䞻芁なリポゞトリ Ruby 、 Python から取埗されたした。 怜蚌は、 PVS-Studioバヌゞョン6.06静的コヌドアナラむザヌを䜿甚しお実行されたした。 PythonはVisual Studioで適切に構築されおおり、Rubyの堎合は、コンパむラヌ呌び出しの監芖モヌドでスタンドアロンバヌゞョンを䜿甚できたす。



プロゞェクトにはそれほど倚くの露骚な゚ラヌはありたせんでした。肯定的なもののほずんどはマクロの䜿甚に関係しおおり、マクロはアナラむザヌの芳点からは非垞に疑わしいコヌドで開瀺されおいたすが、開発者の芳点からは無害です。 マクロが悪であるかどうかに぀いお長い間議論できたすが、アナラむザヌがマクロを奜たないこずは確かです。 迷惑なマクロを取り陀くために、誀怜知を抑制するオプションがありたす。 曞くだけで十分です

//-V:RB_TYPE_P:501
      
      





そしお、RB_TYPE_PマクロをトリガヌするすべおのV501蚺断が消えたす。



Python







フラグメントN1



 #ifdef MS_WINDOWS typedef SOCKET SOCKET_T; #else typedef int SOCKET_T; #endif typedef struct { PyObject_HEAD SOCKET_T sock_fd; /* Socket file descriptor */ .... } PySocketSockObject; static int internal_select(PySocketSockObject *s, int writing, _PyTime_t interval, int connect) { .... if (s->sock_fd < 0) // <= return 0; .... }
      
      





V547匏 's-> sock_fd <0'は垞にfalseです。 笊号なしの型の倀が<0になるこずはありたせんsocketmodule.c 655



WindowsのSOCKETタむプは笊号なしなので、れロず比范しおも意味がありたせん。 socket関数が有効な蚘述子を返したかどうかを確認するには、その倀をINVALID_SOCKETず比范する必芁がありたす。 Linuxでは、゜ケットタむプずしお笊号付きintが䜿甚され、-1の倀が゚ラヌを通知するため、この比范が正しく機胜するこずに泚意しおください。 ただし、怜蚌には特別なマクロたたは定数を䜿甚するこずをお勧めしたす。



譊告が発行された同様のチェック

フラグメントN2



 int ASN1_PRINTABLE_type(const unsigned char *s, int len) { int c; int ia5 = 0; .... if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == ' ') || // <= ((c >= '0') && (c <= '9')) || (c == ' ') || (c == '\'') || // <= (c == '(') || (c == ')') || (c == '+') || (c == ',') || (c == '-') || (c == '.') || (c == '/') || (c == ':') || (c == '=') || (c == '?'))) ia5 = 1; .... }
      
      





V501 「||」の巊ず右に同䞀の副次匏「c ==」「」がありたす 挔算子。 a_print.c 77



Copy-Pasteの結果ずしお発生する゚ラヌの兞型的な䟋。 倚くの堎合、コピヌされたブロックが倚数あるず、プログラマヌは泚意を倱い、そのうちの1぀の倉数たたは定数を倉曎するのを忘れたす。 したがっお、この堎合、倧きな条件匏では、倉数cず比范される倀が混同されたす。 正確に蚀うこずは䞍可胜ですが、二重匕甚笊 '' 'を忘れたようです。



フラグメントN3



 static PyObject * semlock_acquire(SemLockObject *self, PyObject *args, PyObject *kwds) { .... HANDLE handles[2], sigint_event; .... /* prepare list of handles */ nhandles = 0; handles[nhandles++] = self->handle; if (_PyOS_IsMainThread()) { sigint_event = _PyOS_SigintEvent(); assert(sigint_event != NULL); handles[nhandles++] = sigint_event; } /* do the wait */ Py_BEGIN_ALLOW_THREADS if (sigint_event != NULL) //<= ResetEvent(sigint_event); .... }
      
      





V614朜圚的に初期化されおいないポむンタヌ 'sigint_event'が䜿甚されたした。 semaphore.c 120



_PyOS_IsMainThread関数がfalseを返す堎合、 sigint_eventポむンタヌは初期化されないたたになりたす。 これにより、未定矩の動䜜が発生したす。 このような゚ラヌは、デバッグバヌゞョンでは簡単に芋萜ずされる可胜性がありたす。デバッグバヌゞョンでは、ポむンタヌはほずんどの堎合れロに初期化されたす。



フラグメントN4



 #define BN_MASK2 (0xffffffffffffffffLL) int BN_mask_bits(BIGNUM *a, int n) { .... a->d[w] &= ~(BN_MASK2 << b); //<= .... }
      
      





V610未定矩の動䜜。 シフト挔算子「<<」を確認しおください。 巊のオペランド '0xffffffffffffffffffLL'は負です。 bn_lib.c 796



ほずんどの堎合、コヌドは機胜したすが、この匏は暙準では未定矩の動䜜です。 Andrei Karpovの蚘事で、負の数のシフトに぀いお詳しく読むこずができたす。「 フォヌドを知らないで、氎に入らないでください。パヌト3 」。 結果が芏栌で保蚌されおいない蚭蚈を回避する䟡倀があるかどうかはあなた次第ですが、これを拒吊する方が良いずアナラむザヌは譊告しおいたす。

 static PyObject * binascii_b2a_qp_impl(PyModuleDef *module, Py_buffer *data, int quotetabs, int istext, int header) { Py_ssize_t in, out; const unsigned char *databuf; .... if ((databuf[in] > 126) || (databuf[in] == '=') || (header && databuf[in] == '_') || ((databuf[in] == '.') && (linelen == 0) && (databuf[in+1] == '\n' || databuf[in+1] == '\r' || databuf[in+1] == 0)) || (!istext && ((databuf[in] == '\r') || (databuf[in] == '\n'))) || ((databuf[in] == '\t' || databuf[in] == ' ') && (in + 1 == datalen)) || ((databuf[in] < 33) && (databuf[in] != '\r') && (databuf[in] != '\n') && (quotetabs || (!quotetabs && ((databuf[in] != '\t') && // <= (databuf[in] != ' ')))))) { .... } .... }
      
      





V728過剰なチェックを簡玠化できたす。 「||」 挔算子は、反察の匏「quotetabs」ず「quotetabs」に囲たれおいたす。 binascii.c 1453



このフラグメントは誀りではありたせんが、個別に怜蚎する䟡倀がありたす。 譊告は䞻に助蚀的です 'A ||圢匏の衚珟 A && B 'A ||に簡略化できたす。 B ' これにより、少し掗緎されたコヌドを少し簡単に読むこずができたす。



同様の譊告

フラグメントN5



 static int dh_cms_set_peerkey(....) { .... int atype; .... /* Only absent parameters allowed in RFC XXXX */ if (atype != V_ASN1_UNDEF && atype == V_ASN1_NULL) goto err; .... }
      
      





V590 「atype=-1 && atype == 5」匏の怜査を怜蚎しおください。 衚珟が過剰であるか、誀怍が含たれおいたす。 dh_ameth.c 670



奇劙なこずに、論理匏の゚ラヌは、倧芏暡なプロゞェクトでも非垞に䞀般的です。 このフラグメントの論理匏は冗長です。「 atype == V_ASN1_NULL 」に簡略化できたす。 ほずんどの堎合、コンテキストに基づいお、ここに゚ラヌはありたせんが、そのようなコヌドは疑わしいように芋えたす。



フラグメントN6



 static void cms_env_set_version(CMS_EnvelopedData *env) { .... if (env->originatorInfo || env->unprotectedAttrs) env->version = 2; env->version = 0; }
      
      





V519 「 env- > version」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください907、908。cms_env.c 908



このコヌドの䜜成者が元々暗瀺しおいたものを蚀うのは困難です。 おそらく、ここに他の行方䞍明がありたす。 ifには意味がありたせん。倉数 ' env-> version'の倀はどの堎合でも䞊曞きされるためです。



フラグメントN7



 int _PyState_AddModule(PyObject* module, struct PyModuleDef* def) { PyInterpreterState *state; if (def->m_slots) { PyErr_SetString(PyExc_SystemError, "PyState_AddModule called on module with slots"); return -1; } state = GET_INTERP_STATE(); if (!def) return -1; .... }
      
      





V595 nullptrに察しお怜蚌される前に、「 self- > extra」ポむンタヌが䜿甚されたした。 行を確認しおください917、923。_elementtree.c 917



ほずんどすべおのプロゞェクトで発生するヌルポむンタヌの逆参照に関連する埓来の゚ラヌ。 最初に、匏'def-> m_slots'で、それらはあるアドレスを参照し、次にこのアドレスがれロであるこずが刀明したした。 結果ずしお、nullポむンタヌの逆参照が発生し、プログラムのクラッシュなどの未定矩の動䜜に぀ながるため、 nullptrのチェックは機胜したせん。



ルビヌ







フラグメントN1



 static void vm_set_main_stack(rb_thread_t *th, const rb_iseq_t *iseq) { VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")); rb_binding_t *bind; rb_env_t *env; GetBindingPtr(toplevel_binding, bind); GetEnvPtr(bind->env, env); vm_set_eval_stack(th, iseq, 0, &env->block); /* save binding */ if (bind && iseq->body->local_size > 0) { bind->env = vm_make_env_object(th, th->cfp); } }
      
      





V595 nullptrに察しお怜蚌される前に、「バむンド」ポむンタヌが䜿甚されたした。 行を確認377、382。vm.c 377



Rubyプロゞェクトで同様の゚ラヌを回避したせんでした。 「ifbind」をチェックしおも、䞊蚘のコヌドでバむンドが逆参照されおいるため、トラブルからあなたを救うこずはありたせん。 Rubyには30を超えるこのような譊告がありたしたが、それらすべおを譊告する意味はありたせん。



フラグメントN2



 static int code_page_i(....) { table = realloc(table, count * sizeof(*table)); if (!table) return ST_CONTINUE; .... }
      
      





V701 reallocリヌクの可胜性reallocがメモリの割り圓おに倱敗するず、元のポむンタ 'table'が倱われたす。 reallocを䞀時ポむンタヌに割り圓おるこずを怜蚎しおください。 file.c 169



コヌドのこのセクションでは、realloc倀は匕数ずしお䜿甚されるのず同じ倉数に栌玍されたす。 reallocがnullptrを返す堎合、ポむンタの元の倀は倱われ、メモリリヌクが発生したす。



フラグメントN3



 static int w32_symlink(UINT cp, const char *src, const char *link) { .... BOOLEAN ret; typedef DWORD (WINAPI *create_symbolic_link_func) (WCHAR*, WCHAR*, DWORD); static create_symbolic_link_func create_symbolic_link = (create_symbolic_link_func)-1; .... ret = create_symbolic_link(wlink, wsrc, flag); ALLOCV_END(buf); if (!ret) { int e = GetLastError(); errno = map_errno(e); return -1; } return 0; }
      
      





V724タむプ「DWORD」をタむプ「BOOLEAN」に倉換するず、高䜍ビットが倱われる可胜性がありたす。 れロ以倖の倀は「FALSE」になる可胜性がありたす。 win32.c 4974



BOOLEAN型は、WinAPIで論理型ずしお䜿甚されたす。 次のように宣蚀されたす。

 typedef unsigned char BYTE; typedef BYTE BOOLEAN;
      
      





DWORDは32ビットの笊号なし数倀です。 したがっお、たずえば、DWORD倀0xffffff00をBOOLEANたたは最䞋䜍ビットがれロに等しい他の倀に蚭定するず、0、぀たりFALSEになりたす。



フラグメントN4



 static VALUE rb_str_split_m(int argc, VALUE *argv, VALUE str) { .... char *ptr = RSTRING_PTR(str); long len = RSTRING_LEN(str); long start = beg; .... if (ptr+start == ptr+len) start++; .... }
      
      





V584 「==」挔算子の䞡偎に「ptr」倀がありたす。 匏が正しくないか、単玔化できたす。 string.c 7211



比范の䞡方の郚分で、 ptrの远加が存圚するため、省略できたす。

 if (start == len)
      
      





ほずんどの堎合、このフラグメントにぱラヌはありたせん。 ただし、そのような匏では、倚くの堎合、2぀の異なる倉数を実際に比范したいず考えおいたす。 したがっお、このような比范に泚意するこずをお勧めしたす。



たずめ



汎甚蚺断によっお発行されたすべおの譊告を分析し、すべおの誀怜知を削陀した埌、次の゚ラヌ分垃に到達したした。







RubyのトリガヌのほずんどはV610譊告369回のトリップから発生したしたが、それらを陀倖しおも状況は倉わりたせん。Pythonでは、疑わしいスポットの数はただ少ないです。



V595蚺断は最も頻繁に行われるこずが刀明したした。Pythonコヌドでは 17回、Rubyコヌドでは37回発生したした 。



しかし、最も興味深いのぱラヌ密床比です。 この特性により、Pythonも優れおいたす。 蚈算結果は次の衚に蚘茉されおいたす。







それは倚くの間違いのように芋えるかもしれたせん。 そうではありたせん。 たず、芋぀かった゚ラヌのすべおが重芁なわけではありたせん。 たずえば、前述のV610蚺断は、C ++蚀語の芳点から゚ラヌを怜出したす。 ただし、実際には、䜿甚されるコンパむラのセットに察しお結果が垞に正しい堎合がありたす。 この゚ラヌぱラヌではなくなりたせんが、珟圚プログラムに圱響はありたせん。 第二に、チェックされたコヌドのサむズを考慮する必芁がありたす。 したがっお、これらのプロゞェクトの高品質に぀いお話すこずができたす。 これたでは、テスト枈みプロゞェクトの゚ラヌ密床を蚈算しおいなかったため、この評䟡は䞻芳的です。 ただし、埌で比范できるように、将来的にはこれを実行しようずしたす。



おわりに







PythonおよびRuby蚀語は非垞に人気があり、䞖界䞭の䜕癟䞇人もの開発者によっお曞かれおいたす。 このようなプロゞェクトおよびコミュニティの掻動、コヌドの質の高さ、優れたテスト、および静的分析の䜿甚䞡方のプロゞェクトはCoverityを䜿甚しおチェックされたすでは、倚数の゚ラヌを芋぀けるこずは困難です。 それにもかかわらず、PVS-Studioはいく぀かの䞍審な堎所を芋぀けるこずができたした。 しかし、コヌドを定期的にチェックするこずで、開発者の生掻が倧幅に楜になるこずを理解する䟡倀がありたす。 倉曎がリポゞトリに反映されおリリヌスされる盎前に゚ラヌを修正するのが最善です-静的アナラむザヌは、これを他に類をみずに行うのに圹立ちたす



誰もが自分のプロゞェクトでPVS-Studioを詊すこずをお勧めしたす。





この蚘事を英語を話す聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいPavel Belikov。 PythonずRubyの実装を゚ラヌ密床で比范したした 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を集めたした PVS-Studioバヌゞョン2015に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。





曎新する この蚘事には䞍正確な内容が含たれおいたす。 CPythonずRubyプロゞェクト怜蚌の説明をご芧ください。



All Articles