• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "libpandabase/os/native_stack.h"
17 #include "runtime/include/runtime.h"
18 #include "runtime/include/thread_scopes.h"
19 #include "utils/logger.h"
20 #include "utils/span.h"
21 #include "verification/jobs/thread_pool.h"
22 #include "verification/jobs/cache.h"
23 #include "verification/util/is_system.h"
24 #include "generated/base_options.h"
25 
26 // generated
27 #include "ark_version.h"
28 #include "generated/verifier_options_gen.h"
29 
30 #include <csignal>
31 
32 namespace panda::verifier {
33 
RunVerifier(const Options & cli_options)34 bool RunVerifier(const Options &cli_options)
35 {
36     auto &runtime = *Runtime::GetCurrent();
37     auto &class_linker = *runtime.GetClassLinker();
38     {
39         // block to destruct cache_api ASAP
40         auto cache_api = ThreadPool::GetCache()->FastAPI();
41         cache_api.ProcessFiles(class_linker.GetBootPandaFiles());
42         class_linker.EnumeratePandaFiles([&cache_api](const panda_file::File &pf) {
43             cache_api.ProcessFile(pf);
44             return true;
45         });
46     }
47 
48     const std::vector<std::string> &class_names = cli_options.GetClasses();
49     const std::vector<std::string> &method_names = cli_options.GetMethods();
50 
51     std::vector<Method *> enqueued;
52     bool result = true;
53 
54     auto enqueue_method = [&](Method *method) {
55         method->EnqueueForVerification();
56         enqueued.push_back(method);
57     };
58 
59     bool verifyLibraries = runtime.GetVerificationOptions().VerifyRuntimeLibraries;
60 
61     auto enqueue_class = [&](const Class &klass) {
62         // this is already checked in enqueue_method, but faster to skip them in the first place
63         if (!verifyLibraries && IsSystemClass(klass)) {
64             LOG(INFO, VERIFIER) << klass.GetName() << " is a system class, skipping";
65             return;
66         }
67         LOG(INFO, VERIFIER) << "Begin verification of class " << klass.GetName();
68         for (auto &method : klass.GetMethods()) {
69             enqueue_method(&method);
70         }
71     };
72 
73     // we need ScopedManagedCodeThread for verify since it can allocate objects
74     ScopedManagedCodeThread managed_obj_thread(MTManagedThread::GetCurrent());
75     if (class_names.empty() && method_names.empty()) {
76         auto handle_file = [&](const panda_file::File &file) {
77             for (auto id : file.GetClasses()) {
78                 panda_file::File::EntityId entity_id {id};
79                 if (!file.IsExternal(entity_id)) {
80                     auto opt_lang = panda_file::ClassDataAccessor {file, entity_id}.GetSourceLang();
81                     ClassLinkerExtension *ext =
82                         class_linker.GetExtension(opt_lang.value_or(panda_file::SourceLang::PANDA_ASSEMBLY));
83                     if (ext == nullptr) {
84                         LOG(ERROR, VERIFIER) << "Error: Class Linker Extension failed to initialize";
85                         return false;
86                     }
87                     const Class *klass = ext->GetClass(file, entity_id);
88 
89                     if (klass != nullptr) {
90                         enqueue_class(*klass);
91                     }
92                 }
93             }
94             return true;
95         };
96 
97         class_linker.EnumeratePandaFiles(handle_file);
98 
99         if (verifyLibraries) {
100             class_linker.EnumerateBootPandaFiles(handle_file);
101         } else if (runtime.GetPandaFiles().empty()) {
102             // in this case the last boot-panda-file and only it is actually not a system file and should be
103             // verified
104             OptionalConstRef<panda_file::File> file;
105             class_linker.EnumerateBootPandaFiles([&file](const panda_file::File &pf) {
106                 file = std::cref(pf);
107                 return true;
108             });
109             if (file.HasRef()) {
110                 handle_file(*file);
111             } else {
112                 LOG(ERROR, VERIFIER) << "No files given to verify";
113             }
114         }
115     } else {
116         PandaUnorderedMap<std::string, Class *> classes_by_name;
117         ClassLinkerContext *ctx =
118             class_linker.GetExtension(runtime.GetLanguageContext(runtime.GetRuntimeType()))->GetBootContext();
119 
120         // TODO(romanov) make this a simple method in LibCache when switching from ClassLinker
121         auto get_class_by_name = [&](const std::string &class_name) -> Class * {
122             auto it = classes_by_name.find(class_name);
123             if (it != classes_by_name.end()) {
124                 return it->second;
125             }
126 
127             PandaString descriptor;
128             const uint8_t *class_name_bytes =
129                 ClassHelper::GetDescriptor(utf::CStringAsMutf8(class_name.c_str()), &descriptor);
130             Class *klass = class_linker.GetClass(class_name_bytes, true, ctx);
131             if (klass == nullptr) {
132                 LOG(ERROR, VERIFIER) << "Error: Cannot resolve class with name " << class_name;
133                 result = false;
134             }
135 
136             classes_by_name.emplace(class_name, klass);
137             return klass;
138         };
139 
140         for (const auto &class_name : class_names) {
141             Class *klass = get_class_by_name(class_name);
142             // the bad case is already handled in get_class_by_name
143             if (klass != nullptr) {
144                 enqueue_class(*klass);
145             }
146         }
147 
148         for (const std::string &fq_method_name : method_names) {
149             size_t pos = fq_method_name.find_last_of("::");
150             if (pos == std::string::npos) {
151                 LOG(ERROR, VERIFIER) << "Error: Fully qualified method name must contain '::', was " << fq_method_name;
152                 result = false;
153                 break;
154             }
155             // TODO(romanov) make string_view after removing ClassLinker, now needed for get_class_by_name for
156             // technical reasons
157             std::string class_name = fq_method_name.substr(0, pos - 1);
158             std::string_view unqualified_method_name = std::string_view(fq_method_name).substr(pos + 1);
159             if (std::find(class_names.begin(), class_names.end(), class_name) != class_names.end()) {
160                 // this method was already verified while enumerating class_names
161                 continue;
162             }
163             Class *klass = get_class_by_name(class_name);
164             if (klass != nullptr) {
165                 bool method_found = false;
166                 for (auto &method : klass->GetMethods()) {
167                     const char *name_data = utf::Mutf8AsCString(method.GetName().data);
168                     if (std::string_view(name_data) == unqualified_method_name) {
169                         method_found = true;
170                         LOG(INFO, VERIFIER) << "Verification of method '" << fq_method_name << "'";
171                         enqueue_method(&method);
172                     }
173                 }
174                 if (!method_found) {
175                     LOG(ERROR, VERIFIER) << "Error: Cannot resolve method with name " << unqualified_method_name
176                                          << " in class " << class_name;
177                     result = false;
178                 }
179             }
180         }
181     }
182 
183     for (Method *method : enqueued) {
184         if (!method->Verify()) {
185             LOG(ERROR, VERIFIER) << "Error: method " << method->GetFullName(true) << " failed to verify";
186             result = false;
187         }
188     }
189     return result;
190 }
191 
BlockSignals()192 void BlockSignals()
193 {
194 #if defined(PANDA_TARGET_UNIX)
195     sigset_t set;
196     if (sigemptyset(&set) == -1) {
197         LOG(ERROR, RUNTIME) << "sigemptyset failed";
198         return;
199     }
200     int rc = 0;
201 #ifdef PANDA_TARGET_MOBILE
202     rc += sigaddset(&set, SIGPIPE);
203     rc += sigaddset(&set, SIGQUIT);
204     rc += sigaddset(&set, SIGUSR1);
205     rc += sigaddset(&set, SIGUSR2);
206 #endif  // PANDA_TARGET_MOBILE
207     if (rc < 0) {
208         LOG(ERROR, RUNTIME) << "sigaddset failed";
209         return;
210     }
211 
212     if (os::native_stack::g_PandaThreadSigmask(SIG_BLOCK, &set, nullptr) != 0) {
213         LOG(ERROR, RUNTIME) << "g_PandaThreadSigmask failed";
214     }
215 #endif  // PANDA_TARGET_UNIX
216 }
217 
PrintHelp(const PandArgParser & pa_parser)218 void PrintHelp(const PandArgParser &pa_parser)
219 {
220     std::string error = pa_parser.GetErrorString();
221     if (!error.empty()) {
222         error += "\n\n";
223     }
224     std::cerr << error << "Usage: verifier [option...] [file]\n"
225               << "Verify specified Panda files (given by file and --panda-files) "
226               << "or certain classes/methods in them.\n\n"
227               << pa_parser.GetHelpString() << std::endl;
228 }
229 
Main(int argc,const char ** argv)230 int Main(int argc, const char **argv)
231 {
232     BlockSignals();
233     Span<const char *> sp(argv, argc);
234     RuntimeOptions runtime_options(sp[0]);
235     Options cli_options(sp[0]);
236     PandArgParser pa_parser;
237     base_options::Options base_options("");
238 
239     PandArg<bool> help("help", false, "Print this message and exit");
240     PandArg<bool> options("options", false, "Print verifier options");
241     // tail argument
242     PandArg<std::string> file("file", "", "path to pandafile");
243 
244     cli_options.AddOptions(&pa_parser);
245 
246     pa_parser.Add(&help);
247     pa_parser.Add(&options);
248     pa_parser.PushBackTail(&file);
249     pa_parser.EnableTail();
250 
251     if (!pa_parser.Parse(argc, argv)) {
252         PrintHelp(pa_parser);
253         return 1;
254     }
255 
256     if (runtime_options.IsVersion()) {
257         PrintPandaVersion();
258         return 0;
259     }
260 
261     if (help.GetValue()) {
262         PrintHelp(pa_parser);
263         return 0;
264     }
265 
266     if (options.GetValue()) {
267         std::cout << pa_parser.GetRegularArgs() << std::endl;
268         return 0;
269     }
270 
271     auto cli_options_err = cli_options.Validate();
272     if (cli_options_err) {
273         std::cerr << "Error: " << cli_options_err.value().GetMessage() << std::endl;
274         return 1;
275     }
276 
277     auto boot_panda_files = cli_options.GetBootPandaFiles();
278     auto panda_files = cli_options.GetPandaFiles();
279     std::string main_file_name = file.GetValue();
280 
281     if (!main_file_name.empty()) {
282         if (panda_files.empty()) {
283             boot_panda_files.push_back(main_file_name);
284         } else {
285             auto found_iter = std::find_if(panda_files.begin(), panda_files.end(),
286                                            [&](auto &file_name) { return main_file_name == file_name; });
287             if (found_iter == panda_files.end()) {
288                 panda_files.push_back(main_file_name);
289             }
290         }
291     }
292 
293     runtime_options.SetBootPandaFiles(boot_panda_files);
294     runtime_options.SetPandaFiles(panda_files);
295     runtime_options.SetLoadRuntimes(cli_options.GetLoadRuntimes());
296 
297     base_options.SetLogComponents(cli_options.GetLogComponents());
298     base_options.SetLogLevel(cli_options.GetLogLevel());
299     base_options.SetLogStream(cli_options.GetLogStream());
300     base_options.SetLogFile(cli_options.GetLogFile());
301     Logger::Initialize(base_options);
302 
303     runtime_options.SetLimitStandardAlloc(cli_options.IsLimitStandardAlloc());
304     runtime_options.SetInternalAllocatorType(cli_options.GetInternalAllocatorType());
305     runtime_options.SetInternalMemorySizeLimit(cli_options.GetInternalMemorySizeLimit());
306 
307     runtime_options.SetVerificationMode(cli_options.IsDebugMode() ? VerificationMode::DEBUG
308                                                                   : VerificationMode::AHEAD_OF_TIME);
309     runtime_options.SetVerificationConfigFile(cli_options.GetConfigFile());
310     runtime_options.SetVerificationCacheFile(cli_options.GetCacheFile());
311     runtime_options.SetVerificationUpdateCache(cli_options.IsUpdateCache());
312     runtime_options.SetVerifyRuntimeLibraries(cli_options.IsVerifyRuntimeLibraries());
313     uint32_t threads = cli_options.GetThreads();
314     if (threads == 0) {
315         threads = std::thread::hardware_concurrency();
316         // hardware_concurrency can return 0 if the value is not computable or well defined
317         if (threads == 0) {
318             threads = 1;
319         } else if (threads > MAX_THREADS) {
320             threads = MAX_THREADS;
321         }
322     }
323     runtime_options.SetVerificationThreads(threads);
324 
325     if (!Runtime::Create(runtime_options)) {
326         std::cerr << "Error: cannot create runtime" << std::endl;
327         return -1;
328     }
329 
330     int ret = RunVerifier(cli_options) ? 0 : -1;
331 
332     if (cli_options.IsPrintMemoryStatistics()) {
333         std::cout << Runtime::GetCurrent()->GetMemoryStatistics();
334     }
335     if (!Runtime::Destroy()) {
336         std::cerr << "Error: cannot destroy runtime" << std::endl;
337         return -1;
338     }
339     return ret;
340 }
341 
342 }  // namespace panda::verifier
343 
main(int argc,const char ** argv)344 int main(int argc, const char **argv)
345 {
346     return panda::verifier::Main(argc, argv);
347 }
348