1 //===-- flags_parser.cpp ----------------------------------------*- C++ -*-===//
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 #include "flags_parser.h"
10 #include "common.h"
11 #include "report.h"
12 
13 #include <errno.h>
14 #include <limits.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 namespace scudo {
19 
20 class UnknownFlagsRegistry {
21   static const u32 MaxUnknownFlags = 16;
22   const char *UnknownFlagsNames[MaxUnknownFlags];
23   u32 NumberOfUnknownFlags;
24 
25 public:
add(const char * Name)26   void add(const char *Name) {
27     CHECK_LT(NumberOfUnknownFlags, MaxUnknownFlags);
28     UnknownFlagsNames[NumberOfUnknownFlags++] = Name;
29   }
30 
report()31   void report() {
32     if (!NumberOfUnknownFlags)
33       return;
34     Printf("Scudo WARNING: found %d unrecognized flag(s):\n",
35            NumberOfUnknownFlags);
36     for (u32 I = 0; I < NumberOfUnknownFlags; ++I)
37       Printf("    %s\n", UnknownFlagsNames[I]);
38     NumberOfUnknownFlags = 0;
39   }
40 };
41 static UnknownFlagsRegistry UnknownFlags;
42 
reportUnrecognizedFlags()43 void reportUnrecognizedFlags() { UnknownFlags.report(); }
44 
printFlagDescriptions()45 void FlagParser::printFlagDescriptions() {
46   Printf("Available flags for Scudo:\n");
47   for (u32 I = 0; I < NumberOfFlags; ++I)
48     Printf("\t%s\n\t\t- %s\n", Flags[I].Name, Flags[I].Desc);
49 }
50 
isSeparator(char C)51 static bool isSeparator(char C) {
52   return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
53          C == '\r';
54 }
55 
isSeparatorOrNull(char C)56 static bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
57 
skipWhitespace()58 void FlagParser::skipWhitespace() {
59   while (isSeparator(Buffer[Pos]))
60     ++Pos;
61 }
62 
parseFlag()63 void FlagParser::parseFlag() {
64   const uptr NameStart = Pos;
65   while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
66     ++Pos;
67   if (Buffer[Pos] != '=')
68     reportError("expected '='");
69   const char *Name = Buffer + NameStart;
70   const uptr ValueStart = ++Pos;
71   const char *Value;
72   if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
73     const char Quote = Buffer[Pos++];
74     while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
75       ++Pos;
76     if (Buffer[Pos] == 0)
77       reportError("unterminated string");
78     Value = Buffer + ValueStart + 1;
79     ++Pos; // consume the closing quote
80   } else {
81     while (!isSeparatorOrNull(Buffer[Pos]))
82       ++Pos;
83     Value = Buffer + ValueStart;
84   }
85   if (!runHandler(Name, Value, '='))
86     reportError("flag parsing failed.");
87 }
88 
parseFlags()89 void FlagParser::parseFlags() {
90   while (true) {
91     skipWhitespace();
92     if (Buffer[Pos] == 0)
93       break;
94     parseFlag();
95   }
96 }
97 
parseString(const char * S)98 void FlagParser::parseString(const char *S) {
99   if (!S)
100     return;
101   // Backup current parser state to allow nested parseString() calls.
102   const char *OldBuffer = Buffer;
103   const uptr OldPos = Pos;
104   Buffer = S;
105   Pos = 0;
106 
107   parseFlags();
108 
109   Buffer = OldBuffer;
110   Pos = OldPos;
111 }
112 
parseBool(const char * Value,bool * b)113 inline bool parseBool(const char *Value, bool *b) {
114   if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
115       strncmp(Value, "false", 5) == 0) {
116     *b = false;
117     return true;
118   }
119   if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
120       strncmp(Value, "true", 4) == 0) {
121     *b = true;
122     return true;
123   }
124   return false;
125 }
126 
parseStringPair(const char * Name,const char * Value)127 void FlagParser::parseStringPair(const char *Name, const char *Value) {
128   if (!runHandler(Name, Value, '\0'))
129     reportError("flag parsing failed.");
130 }
131 
runHandler(const char * Name,const char * Value,const char Sep)132 bool FlagParser::runHandler(const char *Name, const char *Value,
133                             const char Sep) {
134   for (u32 I = 0; I < NumberOfFlags; ++I) {
135     const uptr Len = strlen(Flags[I].Name);
136     if (strncmp(Name, Flags[I].Name, Len) != 0 || Name[Len] != Sep)
137       continue;
138     bool Ok = false;
139     switch (Flags[I].Type) {
140     case FlagType::FT_bool:
141       Ok = parseBool(Value, reinterpret_cast<bool *>(Flags[I].Var));
142       if (!Ok)
143         reportInvalidFlag("bool", Value);
144       break;
145     case FlagType::FT_int:
146       char *ValueEnd;
147       errno = 0;
148       long V = strtol(Value, &ValueEnd, 10);
149       if (errno != 0 ||                 // strtol failed (over or underflow)
150           V > INT_MAX || V < INT_MIN || // overflows integer
151           // contains unexpected characters
152           (*ValueEnd != '"' && *ValueEnd != '\'' &&
153            !isSeparatorOrNull(*ValueEnd))) {
154         reportInvalidFlag("int", Value);
155         break;
156       }
157       *reinterpret_cast<int *>(Flags[I].Var) = static_cast<int>(V);
158       Ok = true;
159       break;
160     }
161     return Ok;
162   }
163   // Unrecognized flag. This is not a fatal error, we may print a warning later.
164   UnknownFlags.add(Name);
165   return true;
166 }
167 
registerFlag(const char * Name,const char * Desc,FlagType Type,void * Var)168 void FlagParser::registerFlag(const char *Name, const char *Desc, FlagType Type,
169                               void *Var) {
170   CHECK_LT(NumberOfFlags, MaxFlags);
171   Flags[NumberOfFlags].Name = Name;
172   Flags[NumberOfFlags].Desc = Desc;
173   Flags[NumberOfFlags].Type = Type;
174   Flags[NumberOfFlags].Var = Var;
175   ++NumberOfFlags;
176 }
177 
178 } // namespace scudo
179