1 /*
  2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
  3  * Copyright (c) 2019, Google and/or its affiliates. All rights reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  *
 24  */
 25 
 26 #include "precompiled.hpp"
 27 #include "classfile/tsanIgnoreList.hpp"
 28 #include "classfile/symbolTable.hpp"
 29 #include "memory/resourceArea.inline.hpp"
 30 
 31 static const int MAX_LINE_SIZE  = 1024;
 32 
 33 class FieldMatcher : public CHeapObj<mtClass> {
 34  public:
 35   enum Mode {
 36     Exact = 0,
 37     Prefix = 1,
 38     Any = 2,
 39     Unknown = -1
 40   };
 41 
 42   FieldMatcher(const Symbol* class_name, Mode class_mode,
 43                const Symbol* field_name, Mode field_mode, FieldMatcher* next)
 44       : _class_name(class_name),
 45         _field_name(field_name),
 46         _class_mode(class_mode),
 47         _field_mode(field_mode),
 48         _next(next) { }
 49 
 50   // Given a FieldMatcher as the head of linked-list, returns true if any
 51   // FieldMatcher in the list matches.
 52   static bool match_any(FieldMatcher* head,
 53                         const Symbol* class_name,
 54                         const Symbol* field_name) {
 55     while (head) {
 56       if (head->match(class_name, field_name)) {
 57         return true;
 58       }
 59       head = head->_next;
 60     }
 61     return false;
 62   }
 63 
 64  protected:
 65   const Symbol* _class_name;
 66   const Symbol* _field_name;
 67   Mode _class_mode;
 68   Mode _field_mode;
 69   FieldMatcher* _next;
 70 
 71   static bool match(const Symbol* candidate, const Symbol* match, Mode mode) {
 72     ResourceMark rm;
 73     switch (mode) {
 74       case Exact:
 75         return candidate == match;
 76       case Prefix: {
 77         const char* candidate_str = candidate->as_C_string();
 78         const char* match_str = match->as_C_string();
 79         return (strstr(candidate_str, match_str) == candidate_str);
 80       }
 81       case Any:
 82         return true;
 83       default:
 84         return false;
 85     }
 86   }
 87 
 88   bool match(const Symbol* class_name, const Symbol* field_name) {
 89     return (match(class_name, _class_name, _class_mode) &&
 90             match(field_name, _field_name, _field_mode));
 91   }
 92 };
 93 
 94 FieldMatcher* TsanIgnoreList::_exact_match = NULL;
 95 FieldMatcher* TsanIgnoreList::_prefix_match = NULL;
 96 
 97 // Detects the pattern-matching mode based on the presence and location of
 98 // wildcard character, fixes the pattern inplace and returns the
 99 // pattern-matching mode.
100 static FieldMatcher::Mode make_pattern(char* pattern) {
101   const int len = strlen(pattern);
102   // Inverse of Symbol::as_klass_external_name.
103   // Turn all '.'s into '/'s.
104   for (int index = 0; index < len; index++) {
105     if (pattern[index] == '.') {
106       pattern[index] = '/';
107     }
108   }
109 
110   char* asterisk = strstr(pattern, "*");
111   if (asterisk == NULL) {
112     return FieldMatcher::Exact;
113   }
114   if (asterisk - pattern != len - 1) {
115     warning("Unexpected location for '*' in \"%s\". "
116             "Only prefix patterns are supported.", pattern);
117   }
118   if (asterisk == pattern) {
119     return FieldMatcher::Any;
120   }
121   pattern[len - 1] = '\0';
122   return FieldMatcher::Prefix;
123 }
124 
125 void TsanIgnoreList::parse_from_line(char* line) {
126   EXCEPTION_MARK;
127   char class_pattern[MAX_LINE_SIZE], field_pattern[MAX_LINE_SIZE];
128   // Replace '#' with '\0'.
129   {
130     char* comment = strchr(line, '#');
131     if (comment != NULL) {
132       *comment = '\0';
133     }
134   }
135   // Parse line.
136   if (sscanf(line, "%s %s", class_pattern, field_pattern) != 2) {
137     return;
138   }
139   // Get matcher mode from pattern.
140   FieldMatcher::Mode class_mode = make_pattern(class_pattern);
141   FieldMatcher::Mode field_mode = make_pattern(field_pattern);
142   // If we match against Any, no need for a symbol, else create the symbol.
143   Symbol* class_symbol = (class_mode == FieldMatcher::Any) ? NULL :
144       SymbolTable::new_symbol(class_pattern);
145   Symbol* field_symbol = (field_mode == FieldMatcher::Any) ? NULL :
146       SymbolTable::new_symbol(field_pattern);
147   // Add matcher to beginning of linked list.
148   if (class_mode == FieldMatcher::Exact && field_mode == FieldMatcher::Exact) {
149     _exact_match = new FieldMatcher(class_symbol, class_mode, field_symbol,
150                                     field_mode, _exact_match);
151   } else {
152     _prefix_match = new FieldMatcher(class_symbol, class_mode, field_symbol,
153                                      field_mode, _prefix_match);
154   }
155 }
156 
157 void TsanIgnoreList::parse_from_file(FILE* stream) {
158   char line[MAX_LINE_SIZE];
159   while (fgets(line, sizeof(line), stream)) {
160     if (strlen(line) == sizeof(line) - 1) {
161       warning("TSAN ignore file (ThreadSanitizerIgnoreFile) contains a line longer "
162               "than %d. This pattern will be truncated, and the rest of the "
163               "file will not be processed for pattern matching.",
164               MAX_LINE_SIZE);
165       break;
166     }
167     parse_from_line(line);
168   }
169   if (ferror(stream)) {
170     warning("Error reading from TSAN ignore file");
171   }
172 }
173 
174 void TsanIgnoreList::init() {
175   if (ThreadSanitizerIgnoreFile == NULL) {
176     return;
177   }
178 
179   FILE* stream = fopen(ThreadSanitizerIgnoreFile, "rt");
180   if (stream == NULL) {
181     warning("TSAN ignore file (ThreadSanitizerIgnoreFile:%s) not found.",
182             ThreadSanitizerIgnoreFile);
183     return;
184   }
185   parse_from_file(stream);
186   fclose(stream);
187 }
188 
189 bool TsanIgnoreList::match(
190     const Symbol* class_name, const Symbol* field_name,
191     BasicType type) {
192   // Wildcard matches are only for primitive types. References should be
193   // added to list individually since they become release/acquire.
194   if (is_java_primitive(type) &&
195       FieldMatcher::match_any(_prefix_match, class_name, field_name)) {
196     return true;
197   }
198   return FieldMatcher::match_any(_exact_match, class_name, field_name);
199 }