1 //===-- sanitizer_flag_parser.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "sanitizer_flag_parser.h"
14 
15 #include "sanitizer_common.h"
16 #include "sanitizer_flag_parser.h"
17 #include "sanitizer_flags.h"
18 #include "sanitizer_libc.h"
19 
20 namespace __sanitizer {
21 
22 class UnknownFlags {
23   static const int kMaxUnknownFlags = 20;
24   const char *unknown_flags_[kMaxUnknownFlags];
25   int n_unknown_flags_;
26 
27  public:
Add(const char * name)28   void Add(const char *name) {
29     CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
30     unknown_flags_[n_unknown_flags_++] = name;
31   }
32 
Report()33   void Report() {
34     if (!n_unknown_flags_) return;
35     Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
36     for (int i = 0; i < n_unknown_flags_; ++i)
37       Printf("    %s\n", unknown_flags_[i]);
38     n_unknown_flags_ = 0;
39   }
40 };
41 
42 UnknownFlags unknown_flags;
43 
ReportUnrecognizedFlags()44 void ReportUnrecognizedFlags() {
45   unknown_flags.Report();
46 }
47 
ll_strndup(const char * s,uptr n)48 char *FlagParser::ll_strndup(const char *s, uptr n) {
49   uptr len = internal_strnlen(s, n);
50   char *s2 = (char *)GetGlobalLowLevelAllocator().Allocate(len + 1);
51   internal_memcpy(s2, s, len);
52   s2[len] = 0;
53   return s2;
54 }
55 
PrintFlagDescriptions()56 void FlagParser::PrintFlagDescriptions() {
57   char buffer[128];
58   buffer[sizeof(buffer) - 1] = '\0';
59   Printf("Available flags for %s:\n", SanitizerToolName);
60   for (int i = 0; i < n_flags_; ++i) {
61     bool truncated = !(flags_[i].handler->Format(buffer, sizeof(buffer)));
62     CHECK_EQ(buffer[sizeof(buffer) - 1], '\0');
63     const char *truncation_str = truncated ? " Truncated" : "";
64     Printf("\t%s\n\t\t- %s (Current Value%s: %s)\n", flags_[i].name,
65            flags_[i].desc, truncation_str, buffer);
66   }
67 }
68 
fatal_error(const char * err)69 void FlagParser::fatal_error(const char *err) {
70   Printf("%s: ERROR: %s\n", SanitizerToolName, err);
71   Die();
72 }
73 
is_space(char c)74 bool FlagParser::is_space(char c) {
75   return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
76          c == '\r';
77 }
78 
skip_whitespace()79 void FlagParser::skip_whitespace() {
80   while (is_space(buf_[pos_])) ++pos_;
81 }
82 
parse_flag(const char * env_option_name)83 void FlagParser::parse_flag(const char *env_option_name) {
84   uptr name_start = pos_;
85   while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
86   if (buf_[pos_] != '=') {
87     if (env_option_name) {
88       Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName,
89              env_option_name);
90       Die();
91     } else {
92       fatal_error("expected '='");
93     }
94   }
95   char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
96 
97   uptr value_start = ++pos_;
98   char *value;
99   if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
100     char quote = buf_[pos_++];
101     while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
102     if (buf_[pos_] == 0) fatal_error("unterminated string");
103     value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
104     ++pos_; // consume the closing quote
105   } else {
106     while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
107     if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
108       fatal_error("expected separator or eol");
109     value = ll_strndup(buf_ + value_start, pos_ - value_start);
110   }
111 
112   bool res = run_handler(name, value);
113   if (!res) fatal_error("Flag parsing failed.");
114 }
115 
parse_flags(const char * env_option_name)116 void FlagParser::parse_flags(const char *env_option_name) {
117   while (true) {
118     skip_whitespace();
119     if (buf_[pos_] == 0) break;
120     parse_flag(env_option_name);
121   }
122 
123   // Do a sanity check for certain flags.
124   if (common_flags_dont_use.malloc_context_size < 1)
125     common_flags_dont_use.malloc_context_size = 1;
126 }
127 
ParseStringFromEnv(const char * env_name)128 void FlagParser::ParseStringFromEnv(const char *env_name) {
129   const char *env = GetEnv(env_name);
130   VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>");
131   ParseString(env, env_name);
132 }
133 
ParseString(const char * s,const char * env_option_name)134 void FlagParser::ParseString(const char *s, const char *env_option_name) {
135   if (!s) return;
136   // Backup current parser state to allow nested ParseString() calls.
137   const char *old_buf_ = buf_;
138   uptr old_pos_ = pos_;
139   buf_ = s;
140   pos_ = 0;
141 
142   parse_flags(env_option_name);
143 
144   buf_ = old_buf_;
145   pos_ = old_pos_;
146 }
147 
ParseFile(const char * path,bool ignore_missing)148 bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
149   static const uptr kMaxIncludeSize = 1 << 15;
150   char *data;
151   uptr data_mapped_size;
152   error_t err;
153   uptr len;
154   if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
155                         Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
156     if (ignore_missing)
157       return true;
158     Printf("Failed to read options from '%s': error %d\n", path, err);
159     return false;
160   }
161   ParseString(data, path);
162   UnmapOrDie(data, data_mapped_size);
163   return true;
164 }
165 
run_handler(const char * name,const char * value)166 bool FlagParser::run_handler(const char *name, const char *value) {
167   for (int i = 0; i < n_flags_; ++i) {
168     if (internal_strcmp(name, flags_[i].name) == 0)
169       return flags_[i].handler->Parse(value);
170   }
171   // Unrecognized flag. This is not a fatal error, we may print a warning later.
172   unknown_flags.Add(name);
173   return true;
174 }
175 
RegisterHandler(const char * name,FlagHandlerBase * handler,const char * desc)176 void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
177                                  const char *desc) {
178   CHECK_LT(n_flags_, kMaxFlags);
179   flags_[n_flags_].name = name;
180   flags_[n_flags_].desc = desc;
181   flags_[n_flags_].handler = handler;
182   ++n_flags_;
183 }
184 
FlagParser()185 FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
186   flags_ =
187       (Flag *)GetGlobalLowLevelAllocator().Allocate(sizeof(Flag) * kMaxFlags);
188 }
189 
190 }  // namespace __sanitizer
191