
çŽ6ãæã«1åãYandexã®åŸæ¥å¡ã®èª°ããç§ãã¡ã«æžããŠãPVS-Studioã®ã©ã€ã»ã³ã¹ã«èå³ãæã¡ãè©ŠçšçãããŠã³ããŒãããŠæ¶ããŸãã ããã¯æ®éã®ããšã§ãç§ãã¡ã¯ã¢ãã©ã€ã¶ãŒã倧äŒæ¥ã«è²©å£²ããé ãããã»ã¹ã«æ £ããŠããŸãã ãã ããäžåºŠæ©äŒã蚪ããã°ãYandexéçºè ã«æšæ¶ãäŒããPVS-StudioããŒã«ãæãåºãããšã¯äžèŠã§ã¯ãããŸããã
æ£çŽãªãšããããã®èšäºã¯ã»ãšãã©ã©ã³ãã ã§ããã ClickHouseããã§ãã¯ããããã«æ¢ã«ç³ãåºãããŸããããã©ãããããããã®ã¢ã€ãã¢ã¯å¿ããããŸããã å æ¥ãã€ã³ã¿ãŒããããæ©ãåã£ãŠãç§ã¯åã³ClickHouseã®èšåã«åºäŒãããã®ãããžã§ã¯ãã«èå³ãæã¡ãŸããã ä»åãç§ã¯ãã®ãããžã§ã¯ããå éãããã«ãã§ãã¯ããããšã«ããŸããã
ã¯ãªãã¯ããŠã¹
ClickHouseã¯ãOLAPïŒãªã³ã©ã€ã³åæã¯ãšãªåŠçïŒçšã®ã«ã©ã ããŒDBMSã§ãã ClickHouseã¯Yandex.Metricaã®ã¿ã¹ã¯ã解決ããããã«Yandexã§éçºãããŸããã ClickHouseã䜿çšãããšãæŽæ°ãããããŒã¿ã«å¯ŸããŠãªã¢ã«ã¿ã€ã ã§åæã¯ãšãªãå®è¡ã§ããŸãã ãã®ã·ã¹ãã ã¯çŽç·çã«ã¹ã±ãŒã©ãã«ã§ãããæ°å ã®ã¬ã³ãŒããšãã¿ãã€ãã®ããŒã¿ãæ±ãããšãã§ããŸãã 2016幎6æãClickHouseã¯Apache 2.0ã©ã€ã»ã³ã¹ã§ãªãŒãã³ãœãŒã¹ã«ã¢ããããŒããããŸããã
- ãŠã§ããµã€ãïŒ clickhouse.yandex
- ãŠã£ãããã£ã¢ã®ããŒãžïŒ ClickHouse
- Habrahabr Webãµã€ãã®èšäºïŒ YandexãClickHouseãéããŸã ã
- GitHub.comã®ãªããžããªïŒ yandex / ClickHouse
PVS-Studioã䜿çšãããããžã§ã¯ãåæ
2017幎8æ14æ¥ã«ãªããžããªããååŸããClickHouseãœãŒã¹ã³ãŒãã確èªããŸããã æ€èšŒã«ã¯ãPVS-Studio v6.17ã¢ãã©ã€ã¶ãŒã®ããŒã¿çã䜿çšããŸããã ãã®èšäºãå ¬éãããé ã«ã¯ããã®ããŒãžã§ã³ã¯ãã§ã«ãªãªãŒã¹ãããŠããŸãã
次ã®ãã£ã¬ã¯ããªã¯ã¹ãã£ã³ããé€å€ãããŸããã
- ClickHouse / contrib
- ClickHouse / libs
- ã¯ãªãã¯ããŠã¹/ãã«ã
- ClickHouse / dbms / src / Common / testsãªã©ãããŸããŸãªãã¹ããé€å€ãããŸãã
æ®ãã®C ++ãœãŒã¹ã³ãŒãã®ãµã€ãºã¯213 KLOCã§ãã åæã«ã7.9ïŒ ã®è¡ãã³ã¡ã³ãã§ãã ãã¹ãããããããã©ã€ãšã¿ãªãªã³ãŒãã¯ã»ãšãã©ãããŸããïŒçŽ196 KLOCã§ãã
ã芧ã®ãšãããClickHouseãããžã§ã¯ãã®ãµã€ãºã¯å°ããã§ãã åæã«ãã³ãŒãã®å質ã¯éåžžã«é«ããè¡æçãªèšäºãæžãããšã¯ã§ããŸããã ã¢ãã©ã€ã¶ãŒã¯åèšã§130ã®èŠåïŒäžè¬åæãé«ããã³äžã¡ãã»ãŒãžïŒãçºè¡ããŸããã
誀æ€ç¥ã®æ°ã«ã€ããŠçããããšã¯é£ãããšæããŸãã æ£åŒã«falseãšåŒã¶ããšã®ã§ããªãå€ãã®èŠåããããŸããããããã®å®çšçãªå©ç¹ã¯ãããŸããã ãããè¡ãæãç°¡åãªæ¹æ³ã¯ãäŸãæããããšã§ãã
int format_version; .... if (format_version < 1 || format_version > 4) throw Exception("Bad checksums format version: " + ....); if (format_version == 1) return false; if (format_version == 2) return read_v2(in); if (format_version == 3) return read_v3(in); if (format_version == 4) return read_v4(in); return false;
ã¢ãã©ã€ã¶ãŒã¯ãåŒïŒformat_version == 4ïŒã®è©äŸ¡ãéå§ããããšãåžžã«çã«ãªããšããäºå®ã«æ³šç®ããŸãã ã芧ã®ãšããã format_versionã®å€ã[ 1..4 ]ãè¶ ãããšãäŸå€ãã¹ããŒããããšããäžèšã®ãã§ãã¯ããããŸãã ãŸãã return falseã¹ããŒãã¡ã³ãã¯ãŸã£ããå®è¡ãããŸããã
æ£åŒã«ã¯ãã¢ãã©ã€ã¶ãŒã¯æ£ãããããã誀æ€ç¥ã§ããããšãæ£åœåããæ¹æ³ã¯æ確ã§ã¯ãããŸããã äžæ¹ããã®ã³ãŒããæ£ãããåã«ãå®å šåãã§æžãããŠããããšã¯æããã§ãã
ãã®ãããªå Žåãã¢ãã©ã€ã¶ãŒã®èŠåã¯ããŸããŸãªæ¹æ³ã§æå¶ããããã³ãŒããæžãæãããã§ããŸãã ããšãã°ã次ã®ããã«æžãããšãã§ããŸãã
switch(format_version) { case 1: return false; case 2: return read_v2(in); case 3: return read_v3(in); case 4: return read_v4(in); default: throw Exception("Bad checksums format version: " + ....); }
ç§ãåã«èšãããšãã§ããªãèŠåããŸã ãããŸãïŒãšã©ãŒã瀺ããã©ããã ç§ã¯ãã®ãããžã§ã¯ãã«ç²ŸéããŠããããäžéšã®ã³ãŒããã©ã°ã¡ã³ããã©ã®ããã«æ©èœããã®ãããããŸããã ãã®å Žåãæ€èšããŠãã ããã
3ã€ã®æ©èœãæã€ç¹å®ã®ã¹ã³ãŒãããããŸãã
namespace CurrentMemoryTracker { void alloc(Int64 size); void realloc(Int64 old_size, Int64 new_size); void free(Int64 size); }
é¢æ°ã®ä»®åŒæ°ã®ååã¯ãé¢æ°ãå ¥åã§ããã€ãã®ãµã€ãºãåãå¿ èŠãããããšã瀺åããŠããŸãã ã¢ãã©ã€ã¶ãŒã¯ãããšãã°ãæ§é äœã®ãµã€ãºã§ã¯ãªãããã€ã³ã¿ãŒã®ãµã€ãºãallocé¢æ°ã«æž¡ãããå ŽåãçããŸãã
using Large = HyperLogLogCounter<K, Hash, UInt32, DenominatorType>; Large * large = nullptr; .... CurrentMemoryTracker::alloc(sizeof(large));
ã¢ãã©ã€ã¶ãŒã¯ããããééãã§ãããã©ãããç¥ããŸããã ç§ãç¥ããŸããããç§ã®æèŠã§ã¯ããã®ã³ãŒãã¯çãããã§ãã
äžè¬çã«ãç§ã¯ãã®ãããªå Žåã«ã€ããŠã¯è©±ããŸããã ClickHouseéçºè ãèå³ãæã£ãŠããå Žåããããžã§ã¯ããåå¥ã«æ€èšŒããã¢ã©ãŒãã®ãªã¹ãããã詳现ã«èª¿ã¹ãããšãã§ããŸãã ãã®èšäºã§ã¯ãç§ã«ãšã£ãŠæãèå³æ·±ããšæãããã³ãŒããã©ã°ã¡ã³ãã®ã¿ãæ€èšããŸãã
èå³æ·±ãã³ãŒãã¹ãããã
1. CWE-476ïŒNULLãã€ã³ã¿ãŒéåç §ïŒ3ãšã©ãŒïŒ
bool executeForNullThenElse(....) { .... const ColumnUInt8 * cond_col = typeid_cast<const ColumnUInt8 *>(arg_cond.column.get()); .... if (cond_col) { .... } else if (cond_const_col) { .... } else throw Exception( "Illegal column " + cond_col->getName() + // <= " of first argument of function " + getName() + ". Must be ColumnUInt8 or ColumnConstUInt8.", ErrorCodes::ILLEGAL_COLUMN); .... }
PVS-StudioèŠåïŒ V522 nullãã€ã³ã¿ãŒ 'cond_col'ã®éåç §ãè¡ãããå ŽåããããŸãã FunctionsConditional.h 765
ããã§ã¯ããšã©ãŒãçºçããç¶æ³ã¯æ£ããåŠçãããŸããã äŸå€ãã¹ããŒãã代ããã«ãnullãã€ã³ã¿ãŒã¯éåç §ãããŸãã
ãšã©ãŒã¡ãã»ãŒãžãäœæããã«ã¯ãé¢æ°åŒã³åºãcond_col-> getNameïŒïŒãäœæããŸãã cond_colãã€ã³ã¿ãŒã¯ãã«ã«ãªããããããã¯å®è¡ã§ããŸããã
åæ§ã®ãšã©ãŒãããã«ãããŸãïŒV522 nullãã€ã³ã¿ãŒ 'cond_col'ã®éåç §ãè¡ãããå¯èœæ§ããããŸãã FunctionsConditional.h 1061
NULLãã€ã³ã¿ãŒã®äœ¿çšã«é¢ããå¥ã®ããªââãšãŒã·ã§ã³ãæ€èšããŠãã ããã
void processHigherOrderFunction(....) { .... const DataTypeExpression * lambda_type = typeid_cast<const DataTypeExpression *>(types[i].get()); const DataTypes & lambda_argument_types = lambda_type->getArgumentTypes(); if (!lambda_type) throw Exception("Logical error: .....", ErrorCodes::LOGICAL_ERROR); .... }
PVS-StudioèŠåïŒ V595 nullptrã«å¯ŸããŠæ€èšŒãããåã«ããlambda_typeããã€ã³ã¿ãŒã䜿çšãããŸããã è¡ã確èªããŠãã ããïŒ359ã361ãTypeAndConstantInference.cpp 359
ã¯ããã«ã lambda_typeãã€ã³ã¿ãŒã¯éåç §ããããã®åŸã®ã¿ãã§ãã¯ãããŸãã ã³ãŒããä¿®æ£ããã«ã¯ããã€ã³ã¿ãŒãã§ãã¯ãå°ãé«ã移åããå¿ èŠããããŸãã
if (!lambda_type) throw Exception("Logical error: .....", ErrorCodes::LOGICAL_ERROR); const DataTypes & lambda_argument_types = lambda_type->getArgumentTypes();
2. CWE-665ïŒäžé©åãªåæåïŒ1ãšã©ãŒïŒ
struct TryResult { .... explicit TryResult(Entry entry_) : entry(std::move(entry)) // <= , is_usable(true) , is_up_to_date(true) { } .... Entry entry; .... }
V546ã¯ã©ã¹ã®ã¡ã³ããŒã¯ããèªèº«ã§åæåãããŸãïŒ 'entryïŒentryïŒ'ã PoolWithFailoverBase.h 74
ã¿ã€ããã¹ã«ããã ãšã³ããªã¡ã³ãã¯ããèªäœã§åæåããããã®çµæãå®éã«ã¯åæåãããŸããã ã³ãŒããä¿®æ£ããã«ã¯ãã¢ã³ããŒã¹ã³ã¢ãè¿œå ããŸãã
: entry(std::move(entry_))
3. CWE-672ïŒæå¹æéãŸãã¯ãªãªãŒã¹åŸã®ãªãœãŒã¹ã®æäœïŒ1ãšã©ãŒïŒ
using Strings = std::vector<std::string>; .... int mainEntryClickhousePerformanceTest(int argc, char ** argv) { .... Strings input_files; .... for (const String filename : input_files) // <= { FS::path file(filename); if (!FS::exists(file)) throw DB::Exception(....); if (FS::is_directory(file)) { input_files.erase( // <= std::remove(input_files.begin(), // <= input_files.end(), // <= filename) , // <= input_files.end() ); // <= getFilesFromDir(file, input_files, recursive); } else { if (file.extension().string() != ".xml") throw DB::Exception(....); } } .... }
PVS-StudioèŠåïŒç¯å²ããŒã¹ã®forã«ãŒãã§äœ¿çšããããinput_filesãã³ã³ããã®V789ã€ãã¬ãŒã¿ã¯ããæ¶å»ãæ©èœã®åŒã³åºãæã«ç¡å¹ã«ãªããŸãã PerformanceTest.cpp 1471
input_filesã³ã³ãã㯠ãç¯å²ããŒã¹ã®forã«ãŒãã§äœ¿çšãããŸãã åæã«ãã«ãŒãå ã§ã¯ãäžéšã®èŠçŽ ã®åé€ã«ããã³ã³ãããå€æŽãããå ŽåããããŸãã èªè ããããã§ããªãçç±ãç解ããŠããªãå Žåã V789蚺æã®èª¬æãããç解ããããšããå§ãããŸãã
4. CWE-563ïŒäœ¿çšããªãå€æ°ãžã®å²ãåœãŠïŒãæªäœ¿çšå€æ°ãïŒïŒ1ãšã©ãŒïŒ
struct StringRange { const char * first; const char * second; .... StringRange(TokenIterator token_begin, TokenIterator token_end) { if (token_begin == token_end) { first = token_begin->begin; // <= second = token_begin->begin; // <= } TokenIterator token_last = token_end; --token_last; first = token_begin->begin; // <= second = token_last->end; // <= } };
ã¢ãã©ã€ã¶ãŒã¯2ã€ã®èŠåãçæããŸãã
- V519ãæåã®ãå€æ°ã«ã¯é£ç¶ããŠ2åå€ãå²ãåœãŠãããŸãã ããããããã¯ééãã§ãã è¡ã確èªããŠãã ããïŒ26ã33ãStringRange.h 33
- V519ã2çªç®ãã®å€æ°ã«ã¯ãé£ç¶ããŠ2åå€ãå²ãåœãŠãããŸãã ããããããã¯ééãã§ãã è¡ã確èªããŠãã ããïŒ27ã34ãStringRange.h 34
ç¹å®ã®æ¡ä»¶äžã§ã¯ãæåã«æåã®å€æ°ãš2çªç®ã®å€æ°ã«å€token_begin-> beginãå²ãåœãŠãããŸã ã ããã«ããããã®å Žåã§ããããã®å€æ°ã®å€ã¯åã³å€åããŸãã ã»ãšãã©ã®å Žåããã®ã³ãŒãã«ã¯äœããã®è«çãšã©ãŒãå«ãŸããŠããããäœããæ¬ èœããŠããŸãã ããšãã°ã returnã¹ããŒãã¡ã³ãã¯å¿ããããå ŽåããããŸãã
if (token_begin == token_end) { first = token_begin->begin; second = token_begin->begin; return; }
5. CWE-570ïŒåŒã¯åžžã«FalseïŒ2ãšã©ãŒïŒ
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { .... if (!((.....)) || ((left_is_string || left_is_fixed_string) && (.....)) || (left_is_date && right_is_date) || (left_is_date && right_is_string) || (left_is_string && right_is_date) || (left_is_date_time && right_is_date_time) // 1 || (left_is_date_time && right_is_string) // 1 || (left_is_string && right_is_date_time) // 1 || (left_is_date_time && right_is_date_time) // 2 || (left_is_date_time && right_is_string) // 2 || (left_is_string && right_is_date_time) // 2 || (left_is_uuid && right_is_uuid) || (left_is_uuid && right_is_string) || (left_is_string && right_is_uuid) || (left_is_enum && right_is_enum && .....) || (left_is_enum && right_is_string) || (left_is_string && right_is_enum) || (left_tuple && right_tuple && .....) || (arguments[0]->equals(*arguments[1])))) throw Exception(....); .... }
ãã®æ¡ä»¶äžã§ã¯ã3ã€ã®éšååŒã2åç¹°ãè¿ãããŸãã PVS-Studioã®èŠåïŒ
- V501ã€ã³ã¹ã¿ã³ã¹åFunctionComparison <EqualsOpãNameEquals>ïŒã||ãã®å·Šãšå³ã«åäžã®ãµãåŒãïŒleft_is_date_time && right_is_date_timeïŒãããããŸã æŒç®åã FunctionsComparison.h 1057
- V501ã€ã³ã¹ã¿ã³ã¹åFunctionComparison <EqualsOpãNameEquals>ïŒã||ãã®å·Šãšå³ã«åäžã®ãµãåŒãïŒleft_is_date_time && right_is_stringïŒãããããŸã æŒç®åã FunctionsComparison.h 1057
- V501ã€ã³ã¹ã¿ã³ã¹åFunctionComparison <EqualsOpãNameEquals>ïŒã||ãã®å·Šãšå³ã«åäžã®ãµãåŒãïŒleft_is_string && right_is_date_timeïŒãããããŸã æŒç®åã FunctionsComparison.h 1057
2ã€ã®ãªãã·ã§ã³ãå¯èœã§ãã ãŸãããšã©ãŒã¯ãããŸãããæ¡ä»¶ã¯åçŽã«åé·ã§ãããåçŽåã§ããŸãã 2çªç®ã¯ããã§ã®ééãã§ãããäžéšã®æ¡ä»¶ã¯ãã§ãã¯ãããŸããã ãããã«ããŠããèè ã¯ãã®ã³ãŒãããã§ãã¯ã¢ãŠãããå¿ èŠããããŸãã
æ¡ä»¶ãåžžã«falseã§ããå¥ã®ã±ãŒã¹ãèããŸãã
static void ipv6_scan(const char * src, unsigned char * dst) { .... uint16_t val{}; unsigned char * colonp = nullptr; while (const auto ch = *src++) { const auto num = unhex(ch); if (num != -1) { val <<= 4; val |= num; if (val > 0xffffu) // <= return clear_dst(); saw_xdigit = 1; continue; } .... }
PVS-Studioã®èŠå ïŒ V547åŒ 'val> 0xffffu'ã¯åžžã«falseã§ãã 笊å·ãªãshortåã®å€ã®ç¯å²ïŒ[0ã65535]ã FunctionsCoding.h 339
IPv6ã¢ãã¬ã¹ãå«ãæååã解æãããšãç¡å¹ãªIPv6ã¢ãã¬ã¹ã®äžéšãæ£ãããšèªèãããŸãã å€ãFFFF以äžã®16é²åœ¢åŒã®æ°å€ãåºåãæåã®éã«æžã蟌ãããšãã§ãããšäºæ³ãããŸãã æ°å€ã倧ããå Žåãã¢ãã¬ã¹ã¯ééã£ãŠãããšèŠãªãããŸãã ãã®ç¶æ³ãèå¥ããããã«ãã³ãŒãã«ã¯ã ifïŒval> 0xffffuïŒ ããšãããã§ãã¯ããããŸãã ãããã圌女ã¯åããŸããã å€æ°valã¯uint16_tåã§ããã0xFFFFãè¶ ããããšã¯ã§ããŸããã ãã®çµæãé¢æ°ã¯èª€ã£ãã¢ãã¬ã¹ãã飲ã¿èŸŒããããšã«ãªããŸãã ã¢ãã¬ã¹ã®æ¬¡ã®éšåã¯ãåºåãæåã®åã®æåŸã®4ã€ã®16é²æ°ã§ãã
6. CWE-571ã åŒã¯åžžã«çïŒ1ãšã©ãŒïŒ
static void formatIP(UInt32 ip, char *& out) { char * begin = out; for (auto i = 0; i < 3; ++i) *(out++) = 'x'; for (size_t offset = 8; offset <= 24; offset += 8) { if (offset > 0) // <= *(out++) = '.'; /// Get the next byte. UInt32 value = (ip >> offset) & static_cast<UInt32>(255); /// Faster than sprintf. if (value == 0) { *(out++) = '0'; } else { while (value > 0) { *(out++) = '0' + value % 10; value /= 10; } } } /// And reverse. std::reverse(begin, out); *(out++) = '\0'; }
PVS-Studioã®èŠå ïŒ V547åŒ 'offset> 0'ã¯åžžã«trueã§ãã FunctionsCoding.h 649
æ¡ä»¶ã offset> 0 ãã¯åžžã«æºãããããããåžžã«ãã€ã³ããè¿œå ãããŸãã ããã§ã¯ééãã¯ãªããæ€èšŒã¯åã«äžèŠã§ããããã«æããŸãã ãã¡ããããããŸãããã ããã§ããšã©ãŒã§ãªãå Žåã¯ãä»ã®ããã°ã©ããŒãéçã³ãŒãã¢ãã©ã€ã¶ãŒãæ··ä¹±ãããªãããã«ããã§ãã¯ãåé€ããå¿ èŠããããŸãã
ãããã«
ããããããããžã§ã¯ãã®éçºè ã¯ããã®èšäºã«ã¯åæ ãããŠããªãã¢ãã©ã€ã¶ãŒã®èŠåãèŠãããšã§ãå€ãã®ãšã©ãŒãèŠã€ããããšãã§ããã§ãããã ç¹ã«ãããã«ã¡ã¯ããšèšãããã«ãååãªè³æããã£ãã®ã§ã話ãçµäºããŸã:)ã
äžè¬ã«ãClickHouseãããžã§ã¯ãã®éçºè ã®ã³ââãŒãã®é«å質ã«æ³šç®ããããšæããŸãã ãã ããã©ããªã«é«çŽãªéçºè ã§ãã£ãŠãã圌ãã¯ãšã©ãŒã®åœ±é¿ãåããŸããããã®èšäºã§ã¯ããããå床瀺ããŸãã PVS-Studioéçã³ãŒãã¢ãã©ã€ã¶ãŒã¯ãå€ãã®ãšã©ãŒã®é²æ¢ã«åœ¹ç«ã¡ãŸãã éçºè ã¯ãæ°ããã³ãŒããèšè¿°ãããšãã«éçåæãæ倧éã«æŽ»çšããŸãã æ°ããã³ãŒãããã§ãã¯ããçŽåŸã«ã¢ãã©ã€ã¶ãŒã§æ€åºã§ãããšã©ãŒã®ãããã°ã«æéãè²»ããããšã¯æå³ããããŸããã
PVS-StudioãããŠã³ããŒãããŠãè©Šããã ããã
ãã®èšäºãè±èªåã®èŽè¡ãšå ±æãããå Žåã¯ã翻蚳ãžã®ãªã³ã¯ã䜿çšããŠãã ããïŒAndrey Karpovã Yandexéçºè ã«æ¬æãè¡šããŸã
èšäºãèªãã§è³ªåããããŸããïŒ
å€ãã®å Žåãèšäºã«ã¯åã質åãå¯ããããŸãã ããã§åçãåéããŸããïŒ PVS-StudioããŒãžã§ã³2015ã«é¢ããèšäºã®èªè
ããã®è³ªåãžã®åç ã ãªã¹ããã芧ãã ããã