168d75effSDimitry Andric //===-- sanitizer_suppressions.cpp ----------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // Suppression parsing/matching code. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric //===----------------------------------------------------------------------===// 1268d75effSDimitry Andric 1368d75effSDimitry Andric #include "sanitizer_suppressions.h" 1468d75effSDimitry Andric 1568d75effSDimitry Andric #include "sanitizer_allocator_internal.h" 1668d75effSDimitry Andric #include "sanitizer_common.h" 1768d75effSDimitry Andric #include "sanitizer_flags.h" 1868d75effSDimitry Andric #include "sanitizer_file.h" 1968d75effSDimitry Andric #include "sanitizer_libc.h" 2068d75effSDimitry Andric #include "sanitizer_placement_new.h" 2168d75effSDimitry Andric 2268d75effSDimitry Andric namespace __sanitizer { 2368d75effSDimitry Andric 2468d75effSDimitry Andric SuppressionContext::SuppressionContext(const char *suppression_types[], 2568d75effSDimitry Andric int suppression_types_num) 2668d75effSDimitry Andric : suppression_types_(suppression_types), 2768d75effSDimitry Andric suppression_types_num_(suppression_types_num), 2868d75effSDimitry Andric can_parse_(true) { 2968d75effSDimitry Andric CHECK_LE(suppression_types_num_, kMaxSuppressionTypes); 3068d75effSDimitry Andric internal_memset(has_suppression_type_, 0, suppression_types_num_); 3168d75effSDimitry Andric } 3268d75effSDimitry Andric 3368d75effSDimitry Andric #if !SANITIZER_FUCHSIA 3468d75effSDimitry Andric static bool GetPathAssumingFileIsRelativeToExec(const char *file_path, 3568d75effSDimitry Andric /*out*/char *new_file_path, 3668d75effSDimitry Andric uptr new_file_path_size) { 37fe6060f1SDimitry Andric InternalMmapVector<char> exec(kMaxPathLength); 3868d75effSDimitry Andric if (ReadBinaryNameCached(exec.data(), exec.size())) { 3968d75effSDimitry Andric const char *file_name_pos = StripModuleName(exec.data()); 4068d75effSDimitry Andric uptr path_to_exec_len = file_name_pos - exec.data(); 4168d75effSDimitry Andric internal_strncat(new_file_path, exec.data(), 4268d75effSDimitry Andric Min(path_to_exec_len, new_file_path_size - 1)); 4368d75effSDimitry Andric internal_strncat(new_file_path, file_path, 4468d75effSDimitry Andric new_file_path_size - internal_strlen(new_file_path) - 1); 4568d75effSDimitry Andric return true; 4668d75effSDimitry Andric } 4768d75effSDimitry Andric return false; 4868d75effSDimitry Andric } 4968d75effSDimitry Andric 5068d75effSDimitry Andric static const char *FindFile(const char *file_path, 5168d75effSDimitry Andric /*out*/char *new_file_path, 5268d75effSDimitry Andric uptr new_file_path_size) { 5368d75effSDimitry Andric // If we cannot find the file, check if its location is relative to 5468d75effSDimitry Andric // the location of the executable. 5568d75effSDimitry Andric if (!FileExists(file_path) && !IsAbsolutePath(file_path) && 5668d75effSDimitry Andric GetPathAssumingFileIsRelativeToExec(file_path, new_file_path, 5768d75effSDimitry Andric new_file_path_size)) { 5868d75effSDimitry Andric return new_file_path; 5968d75effSDimitry Andric } 6068d75effSDimitry Andric return file_path; 6168d75effSDimitry Andric } 6268d75effSDimitry Andric #else 6368d75effSDimitry Andric static const char *FindFile(const char *file_path, char *, uptr) { 6468d75effSDimitry Andric return file_path; 6568d75effSDimitry Andric } 6668d75effSDimitry Andric #endif 6768d75effSDimitry Andric 6868d75effSDimitry Andric void SuppressionContext::ParseFromFile(const char *filename) { 6968d75effSDimitry Andric if (filename[0] == '\0') 7068d75effSDimitry Andric return; 7168d75effSDimitry Andric 72fe6060f1SDimitry Andric InternalMmapVector<char> new_file_path(kMaxPathLength); 7368d75effSDimitry Andric filename = FindFile(filename, new_file_path.data(), new_file_path.size()); 7468d75effSDimitry Andric 7568d75effSDimitry Andric // Read the file. 7668d75effSDimitry Andric VPrintf(1, "%s: reading suppressions file at %s\n", 7768d75effSDimitry Andric SanitizerToolName, filename); 7868d75effSDimitry Andric char *file_contents; 7968d75effSDimitry Andric uptr buffer_size; 8068d75effSDimitry Andric uptr contents_size; 8168d75effSDimitry Andric if (!ReadFileToBuffer(filename, &file_contents, &buffer_size, 8268d75effSDimitry Andric &contents_size)) { 8368d75effSDimitry Andric Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName, 8468d75effSDimitry Andric filename); 8568d75effSDimitry Andric Die(); 8668d75effSDimitry Andric } 8768d75effSDimitry Andric 8868d75effSDimitry Andric Parse(file_contents); 89*0fca6ea1SDimitry Andric UnmapOrDie(file_contents, buffer_size); 9068d75effSDimitry Andric } 9168d75effSDimitry Andric 9268d75effSDimitry Andric bool SuppressionContext::Match(const char *str, const char *type, 9368d75effSDimitry Andric Suppression **s) { 9468d75effSDimitry Andric can_parse_ = false; 9568d75effSDimitry Andric if (!HasSuppressionType(type)) 9668d75effSDimitry Andric return false; 9768d75effSDimitry Andric for (uptr i = 0; i < suppressions_.size(); i++) { 9868d75effSDimitry Andric Suppression &cur = suppressions_[i]; 9968d75effSDimitry Andric if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) { 10068d75effSDimitry Andric *s = &cur; 10168d75effSDimitry Andric return true; 10268d75effSDimitry Andric } 10368d75effSDimitry Andric } 10468d75effSDimitry Andric return false; 10568d75effSDimitry Andric } 10668d75effSDimitry Andric 10768d75effSDimitry Andric static const char *StripPrefix(const char *str, const char *prefix) { 10868d75effSDimitry Andric while (*str && *str == *prefix) { 10968d75effSDimitry Andric str++; 11068d75effSDimitry Andric prefix++; 11168d75effSDimitry Andric } 11268d75effSDimitry Andric if (!*prefix) 11368d75effSDimitry Andric return str; 11468d75effSDimitry Andric return 0; 11568d75effSDimitry Andric } 11668d75effSDimitry Andric 11768d75effSDimitry Andric void SuppressionContext::Parse(const char *str) { 11868d75effSDimitry Andric // Context must not mutate once Match has been called. 11968d75effSDimitry Andric CHECK(can_parse_); 12068d75effSDimitry Andric const char *line = str; 12168d75effSDimitry Andric while (line) { 12268d75effSDimitry Andric while (line[0] == ' ' || line[0] == '\t') 12368d75effSDimitry Andric line++; 12468d75effSDimitry Andric const char *end = internal_strchr(line, '\n'); 12568d75effSDimitry Andric if (end == 0) 12668d75effSDimitry Andric end = line + internal_strlen(line); 12768d75effSDimitry Andric if (line != end && line[0] != '#') { 12868d75effSDimitry Andric const char *end2 = end; 12968d75effSDimitry Andric while (line != end2 && 13068d75effSDimitry Andric (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r')) 13168d75effSDimitry Andric end2--; 13268d75effSDimitry Andric int type; 13368d75effSDimitry Andric for (type = 0; type < suppression_types_num_; type++) { 13468d75effSDimitry Andric const char *next_char = StripPrefix(line, suppression_types_[type]); 13568d75effSDimitry Andric if (next_char && *next_char == ':') { 13668d75effSDimitry Andric line = ++next_char; 13768d75effSDimitry Andric break; 13868d75effSDimitry Andric } 13968d75effSDimitry Andric } 14068d75effSDimitry Andric if (type == suppression_types_num_) { 141cb14a3feSDimitry Andric Printf("%s: failed to parse suppressions.\n", SanitizerToolName); 142cb14a3feSDimitry Andric Printf("Supported suppression types are:\n"); 143cb14a3feSDimitry Andric for (type = 0; type < suppression_types_num_; type++) 144cb14a3feSDimitry Andric Printf("- %s\n", suppression_types_[type]); 14568d75effSDimitry Andric Die(); 14668d75effSDimitry Andric } 14768d75effSDimitry Andric Suppression s; 14868d75effSDimitry Andric s.type = suppression_types_[type]; 14968d75effSDimitry Andric s.templ = (char*)InternalAlloc(end2 - line + 1); 15068d75effSDimitry Andric internal_memcpy(s.templ, line, end2 - line); 15168d75effSDimitry Andric s.templ[end2 - line] = 0; 15268d75effSDimitry Andric suppressions_.push_back(s); 15368d75effSDimitry Andric has_suppression_type_[type] = true; 15468d75effSDimitry Andric } 15568d75effSDimitry Andric if (end[0] == 0) 15668d75effSDimitry Andric break; 15768d75effSDimitry Andric line = end + 1; 15868d75effSDimitry Andric } 15968d75effSDimitry Andric } 16068d75effSDimitry Andric 16168d75effSDimitry Andric uptr SuppressionContext::SuppressionCount() const { 16268d75effSDimitry Andric return suppressions_.size(); 16368d75effSDimitry Andric } 16468d75effSDimitry Andric 16568d75effSDimitry Andric bool SuppressionContext::HasSuppressionType(const char *type) const { 16668d75effSDimitry Andric for (int i = 0; i < suppression_types_num_; i++) { 16768d75effSDimitry Andric if (0 == internal_strcmp(type, suppression_types_[i])) 16868d75effSDimitry Andric return has_suppression_type_[i]; 16968d75effSDimitry Andric } 17068d75effSDimitry Andric return false; 17168d75effSDimitry Andric } 17268d75effSDimitry Andric 17368d75effSDimitry Andric const Suppression *SuppressionContext::SuppressionAt(uptr i) const { 17468d75effSDimitry Andric CHECK_LT(i, suppressions_.size()); 17568d75effSDimitry Andric return &suppressions_[i]; 17668d75effSDimitry Andric } 17768d75effSDimitry Andric 17868d75effSDimitry Andric void SuppressionContext::GetMatched( 17968d75effSDimitry Andric InternalMmapVector<Suppression *> *matched) { 18068d75effSDimitry Andric for (uptr i = 0; i < suppressions_.size(); i++) 18168d75effSDimitry Andric if (atomic_load_relaxed(&suppressions_[i].hit_count)) 18268d75effSDimitry Andric matched->push_back(&suppressions_[i]); 18368d75effSDimitry Andric } 18468d75effSDimitry Andric 18568d75effSDimitry Andric } // namespace __sanitizer 186