1 /**
2  * Copyright (c) 2021-2024 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 "monitor.h"
18 #include "os/mutex.h"
19 #include "runtime/include/runtime.h"
20 #include "runtime/include/thread_scopes.h"
21 #include "utils/logger.h"
22 #include "utils/span.h"
23 #include "verification/plugins.h"
24 #include "verification/util/is_system.h"
25 #include "verification/util/optional_ref.h"
26 #include "generated/base_options.h"
27 
28 // generated
29 #include "ark_version.h"
30 #include "generated/verifier_options_gen.h"
31 
32 #include <csignal>
33 #include <chrono>
34 #include <thread>
35 
36 namespace ark::verifier {
37 
38 size_t const MAX_THREADS = 64;
39 
Worker(PandaDeque<Method *> *queue, os::memory::Mutex *lock, size_t threadNum, std::atomic<bool> *result)40 void Worker(PandaDeque<Method *> *queue, os::memory::Mutex *lock, size_t threadNum, std::atomic<bool> *result)
41 {
42     LOG(DEBUG, VERIFIER) << "Verifier thread " << threadNum << " starting";
43     {
44         std::stringstream ss;
45         ss << "Verifier #" << threadNum;
46         if (os::thread::SetThreadName(os::thread::GetNativeHandle(), ss.str().c_str()) != 0) {
47             LOG(ERROR, VERIFIER) << "Failed to set worker thread name " << ss.str();
48         }
49     }
50 
51     auto *service = Runtime::GetCurrent()->GetVerifierService();
52     auto options = Runtime::GetCurrent()->GetOptions();
53     auto mode = options.GetVerificationMode();
54 
55     ManagedThread *thread = nullptr;
56     panda_file::SourceLang currentLang = panda_file::SourceLang::INVALID;
57     for (;;) {
58         Method *method;
59         {
60             os::memory::LockHolder lh {*lock};
61             if (queue->empty()) {
62                 break;
63             }
64             method = queue->front();
65             queue->pop_front();
66         }
67         auto methodLang = method->GetClass()->GetSourceLang();
68         if (methodLang != currentLang) {
69             if (thread != nullptr) {
70                 plugin::GetLanguagePlugin(currentLang)->DestroyManagedThread(thread);
71             }
72             thread = plugin::GetLanguagePlugin(methodLang)->CreateManagedThread();
73             currentLang = methodLang;
74         }
75         if (ark::verifier::Verify(service, method, mode) != Status::OK) {
76             *result = false;
77             LOG(ERROR, VERIFIER) << "Error: method " << method->GetFullName(true) << " failed to verify";
78         }
79     }
80     if (thread != nullptr) {
81         plugin::GetLanguagePlugin(currentLang)->DestroyManagedThread(thread);
82     }
83     LOG(DEBUG, VERIFIER) << "Verifier thread " << threadNum << " finishing";
84 }
85 
EnqueueMethod(Method *method, PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)86 static void EnqueueMethod(Method *method, PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)
87 {
88     if (methodsSet.count(method) == 0) {
89         queue.push_back(method);
90         methodsSet.insert(method);
91     }
92 }
93 
EnqueueClass(const Class &klass, bool verifyLibraries, PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)94 static void EnqueueClass(const Class &klass, bool verifyLibraries, PandaDeque<Method *> &queue,
95                          PandaUnorderedSet<Method *> &methodsSet)
96 {
97     if (!verifyLibraries && IsSystemClass(&klass)) {
98         LOG(INFO, VERIFIER) << klass.GetName() << " is a system class, skipping";
99         return;
100     }
101     LOG(INFO, VERIFIER) << "Begin verification of class " << klass.GetName();
102     for (auto &method : klass.GetMethods()) {
103         EnqueueMethod(&method, queue, methodsSet);
104     }
105 }
106 
GetFileHandler(std::atomic<bool> &result, ClassLinker &classLinker, bool verifyLibraries, PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)107 static auto GetFileHandler(std::atomic<bool> &result, ClassLinker &classLinker, bool verifyLibraries,
108                            PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)
109 {
110     auto handleClass = [&result, &classLinker, verifyLibraries, &queue,
111                         &methodsSet](const panda_file::File &file, const panda_file::File::EntityId &entityId) {
112         if (!file.IsExternal(entityId)) {
113             auto optLang = panda_file::ClassDataAccessor {file, entityId}.GetSourceLang();
114             if (optLang.has_value() && !IsValidSourceLang(optLang.value())) {
115                 LOG(ERROR, VERIFIER) << "Unknown SourceLang";
116                 result = false;
117                 return false;
118             }
119             ClassLinkerExtension *ext =
120                 classLinker.GetExtension(optLang.value_or(panda_file::SourceLang::PANDA_ASSEMBLY));
121             if (ext == nullptr) {
122                 LOG(ERROR, VERIFIER) << "Error: Class Linker Extension failed to initialize";
123                 result = false;
124                 return false;
125             }
126             const Class *klass = ext->GetClass(file, entityId);
127 
128             if (klass != nullptr) {
129                 EnqueueClass(*klass, verifyLibraries, queue, methodsSet);
130             }
131         }
132 
133         return true;
134     };
135 
136     return [handleClass](const panda_file::File &file) {
137         LOG(INFO, VERIFIER) << "Processing file" << file.GetFilename();
138         for (auto id : file.GetClasses()) {
139             panda_file::File::EntityId entityId {id};
140             if (!handleClass(file, entityId)) {
141                 return false;
142             }
143         }
144 
145         return true;
146     };
147 }
148 
VerifyAllNames(std::atomic<bool> &result, bool verifyLibraries, Runtime &runtime, PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)149 static void VerifyAllNames(std::atomic<bool> &result, bool verifyLibraries, Runtime &runtime,
150                            PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)
151 {
152     auto &classLinker = *runtime.GetClassLinker();
153     PandaVector<const panda_file::File *> pfiles;
154     classLinker.EnumeratePandaFiles([&pfiles](const panda_file::File &file) {
155         pfiles.push_back(&file);
156         return true;
157     });
158 
159     auto handleFile = GetFileHandler(result, classLinker, verifyLibraries, queue, methodsSet);
160 
161     for (auto pf : pfiles) {
162         if (!handleFile(*pf)) {
163             break;
164         }
165     }
166 
167     if (verifyLibraries) {
168         classLinker.EnumerateBootPandaFiles(handleFile);
169     } else if (runtime.GetPandaFiles().empty()) {
170         // in this case the last boot-panda-file and only it is actually not a system file and should be
171         // verified
172         OptionalConstRef<panda_file::File> file;
173         classLinker.EnumerateBootPandaFiles([&file](const panda_file::File &pf) {
174             file = std::cref(pf);
175             return true;
176         });
177         if (file.HasRef()) {
178             handleFile(*file);
179         } else {
180             LOG(ERROR, VERIFIER) << "No files given to verify";
181         }
182     }
183 }
184 
GetClassByName(std::atomic<bool> &result, PandaUnorderedMap<std::string, Class *> &classesByName, Runtime &runtime, const std::string &className)185 static Class *GetClassByName(std::atomic<bool> &result, PandaUnorderedMap<std::string, Class *> &classesByName,
186                              Runtime &runtime, const std::string &className)
187 {
188     auto it = classesByName.find(className);
189     if (it != classesByName.end()) {
190         return it->second;
191     }
192 
193     PandaString descriptor;
194     auto &classLinker = *runtime.GetClassLinker();
195     auto *ctx = classLinker.GetExtension(runtime.GetLanguageContext(runtime.GetRuntimeType()))->GetBootContext();
196     const uint8_t *classNameBytes = ClassHelper::GetDescriptor(utf::CStringAsMutf8(className.c_str()), &descriptor);
197 
198     Class *klass = classLinker.GetClass(classNameBytes, true, ctx);
199     if (klass == nullptr) {
200         LOG(ERROR, VERIFIER) << "Error: Cannot resolve class with name " << className;
201         result = false;
202     }
203 
204     classesByName.emplace(className, klass);
205     return klass;
206 }
207 
VeifyMethod(Class *klass, const std::string &fqMethodName, const std::string_view &unqualifiedMethodName, PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)208 static bool VeifyMethod(Class *klass, const std::string &fqMethodName, const std::string_view &unqualifiedMethodName,
209                         PandaDeque<Method *> &queue, PandaUnorderedSet<Method *> &methodsSet)
210 {
211     bool methodFound = false;
212     for (auto &method : klass->GetMethods()) {
213         const char *nameData = utf::Mutf8AsCString(method.GetName().data);
214         if (std::string_view(nameData) == unqualifiedMethodName) {
215             methodFound = true;
216             LOG(INFO, VERIFIER) << "Verification of method '" << fqMethodName << "'";
217             EnqueueMethod(&method, queue, methodsSet);
218         }
219     }
220     return methodFound;
221 }
222 
RunVerifierImpl(std::atomic<bool> &result, Runtime &runtime, const std::vector<std::string> &classNames, const std::vector<std::string> &methodNames, PandaDeque<Method *> &queue)223 static void RunVerifierImpl(std::atomic<bool> &result, Runtime &runtime, const std::vector<std::string> &classNames,
224                             const std::vector<std::string> &methodNames, PandaDeque<Method *> &queue)
225 {
226     bool verifyLibraries = runtime.GetOptions().IsVerifyRuntimeLibraries();
227     PandaUnorderedSet<Method *> methodsSet;
228 
229     // we need ScopedManagedCodeThread for the verifier since it can allocate objects
230     ScopedManagedCodeThread managedObjThread(ManagedThread::GetCurrent());
231     if (classNames.empty() && methodNames.empty()) {
232         VerifyAllNames(result, verifyLibraries, runtime, queue, methodsSet);
233     } else {
234         PandaUnorderedMap<std::string, Class *> classesByName;
235 
236         for (const auto &className : classNames) {
237             Class *klass = GetClassByName(result, classesByName, runtime, className);
238             // the bad case is already handled in get_class_by_name
239             if (klass != nullptr) {
240                 EnqueueClass(*klass, verifyLibraries, queue, methodsSet);
241             }
242         }
243 
244         for (const std::string &fqMethodName : methodNames) {
245             size_t pos = fqMethodName.find_last_of("::");
246             if (pos == std::string::npos) {
247                 LOG(ERROR, VERIFIER) << "Error: Fully qualified method name must contain '::', was " << fqMethodName;
248                 result = false;
249                 break;
250             }
251             std::string className = fqMethodName.substr(0, pos - 1);
252             std::string_view unqualifiedMethodName = std::string_view(fqMethodName).substr(pos + 1);
253             if (std::find(classNames.begin(), classNames.end(), className) != classNames.end()) {
254                 // this method was already verified while enumerating class_names
255                 continue;
256             }
257             Class *klass = GetClassByName(result, classesByName, runtime, className);
258             if (klass == nullptr) {
259                 continue;
260             }
261             bool methodFound = VeifyMethod(klass, fqMethodName, unqualifiedMethodName, queue, methodsSet);
262             if (!methodFound) {
263                 LOG(ERROR, VERIFIER) << "Error: Cannot resolve method with name " << unqualifiedMethodName
264                                      << " in class " << className;
265                 result = false;
266             }
267         }
268     }
269 }
270 
RunVerifier(const Options &cliOptions)271 bool RunVerifier(const Options &cliOptions)
272 {
273     auto isPerfMeasure = cliOptions.IsPerfMeasure();
274     std::chrono::steady_clock::time_point begin;
275     std::chrono::steady_clock::time_point end;
276 
277     os::memory::Mutex lock;
278 
279     auto &runtime = *Runtime::GetCurrent();
280 
281     PandaDeque<Method *> queue;
282 
283     const std::vector<std::string> &classNames = cliOptions.GetClasses();
284     const std::vector<std::string> &methodNames = cliOptions.GetMethods();
285 
286     std::atomic<bool> result = true;
287     RunVerifierImpl(result, runtime, classNames, methodNames, queue);
288 
289     if (isPerfMeasure) {
290         begin = std::chrono::steady_clock::now();
291     }
292 
293     size_t nthreads = runtime.GetOptions().GetVerificationThreads();
294     std::vector<std::thread *> threads;
295     for (size_t i = 0; i < nthreads; i++) {
296         auto *worker = runtime.GetInternalAllocator()->New<std::thread>(Worker, &queue, &lock, i, &result);
297         threads.push_back(worker);
298     }
299 
300     for (auto *thr : threads) {
301         thr->join();
302         runtime.GetInternalAllocator()->Delete(thr);
303     }
304 
305     if (isPerfMeasure) {
306         end = std::chrono::steady_clock::now();
307         std::cout << "Verification time = "
308                   << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << " us" << std::endl;
309     }
310 
311     return result;
312 }
313 
PrintHelp(const PandArgParser &paParser)314 void PrintHelp(const PandArgParser &paParser)
315 {
316     std::string error = paParser.GetErrorString();
317     if (!error.empty()) {
318         error += "\n\n";
319     }
320     std::cerr << error << "Usage: verifier [option...] [file]\n"
321               << "Verify specified Panda files (given by file and --panda-files) "
322               << "or certain classes/methods in them.\n\n"
323               << paParser.GetHelpString() << std::endl;
324 }
325 
RunThreads(Options &cliOptions, RuntimeOptions &runtimeOptions)326 static int RunThreads(Options &cliOptions, RuntimeOptions &runtimeOptions)
327 {
328     uint32_t threads = cliOptions.GetThreads();
329     if (threads == 0) {
330         threads = std::thread::hardware_concurrency();
331         // hardware_concurrency can return 0 if the value is not computable or well defined
332         if (threads == 0) {
333             threads = 1;
334         } else if (threads > MAX_THREADS) {
335             threads = MAX_THREADS;
336         }
337     }
338     runtimeOptions.SetVerificationThreads(threads);
339 
340     if (!Runtime::Create(runtimeOptions)) {
341         std::cerr << "Error: cannot create runtime" << std::endl;
342         return -1;
343     }
344 
345     int ret = RunVerifier(cliOptions) ? 0 : -1;
346 
347     if (cliOptions.IsPrintMemoryStatistics()) {
348         std::cout << Runtime::GetCurrent()->GetMemoryStatistics();
349     }
350     if (!Runtime::Destroy()) {
351         std::cerr << "Error: cannot destroy runtime" << std::endl;
352         return -1;
353     }
354     return ret;
355 }
356 
Run(Options &cliOptions, RuntimeOptions &runtimeOptions, base_options::Options &baseOptions, PandArg<std::string> &file)357 static int Run(Options &cliOptions, RuntimeOptions &runtimeOptions, base_options::Options &baseOptions,
358                PandArg<std::string> &file)
359 {
360     auto bootPandaFiles = cliOptions.GetBootPandaFiles();
361     auto pandaFiles = cliOptions.GetPandaFiles();
362     std::string mainFileName = file.GetValue();
363     if (!mainFileName.empty()) {
364         if (pandaFiles.empty()) {
365             bootPandaFiles.push_back(mainFileName);
366         } else {
367             auto foundIter = std::find_if(pandaFiles.begin(), pandaFiles.end(),
368                                           [&mainFileName](auto &fileName) { return mainFileName == fileName; });
369             if (foundIter == pandaFiles.end()) {
370                 pandaFiles.push_back(mainFileName);
371             }
372         }
373     }
374 
375     runtimeOptions.SetBootPandaFiles(bootPandaFiles);
376     runtimeOptions.SetPandaFiles(pandaFiles);
377     runtimeOptions.SetLoadRuntimes(cliOptions.GetLoadRuntimes());
378     runtimeOptions.SetGcType(cliOptions.GetGcType());
379 
380     baseOptions.SetLogComponents(cliOptions.GetLogComponents());
381     baseOptions.SetLogLevel(cliOptions.GetLogLevel());
382     baseOptions.SetLogStream(cliOptions.GetLogStream());
383     baseOptions.SetLogFile(cliOptions.GetLogFile());
384     Logger::Initialize(baseOptions);
385 
386     runtimeOptions.SetLimitStandardAlloc(cliOptions.IsLimitStandardAlloc());
387     runtimeOptions.SetInternalAllocatorType(cliOptions.GetInternalAllocatorType());
388     runtimeOptions.SetInternalMemorySizeLimit(cliOptions.GetInternalMemorySizeLimit());
389 
390     runtimeOptions.SetVerificationMode(cliOptions.IsDebugMode() ? VerificationMode::DEBUG
391                                                                 : VerificationMode::AHEAD_OF_TIME);
392     runtimeOptions.SetVerificationConfigFile(cliOptions.GetConfigFile());
393     runtimeOptions.SetVerificationCacheFile(cliOptions.GetCacheFile());
394     runtimeOptions.SetVerificationUpdateCache(cliOptions.IsUpdateCache());
395     runtimeOptions.SetVerifyRuntimeLibraries(cliOptions.IsVerifyRuntimeLibraries());
396     return RunThreads(cliOptions, runtimeOptions);
397 }
398 
Main(int argc, const char **argv)399 int Main(int argc, const char **argv)
400 {
401     Span<const char *> sp(argv, argc);
402     RuntimeOptions runtimeOptions(sp[0]);
403     Options cliOptions(sp[0]);
404     PandArgParser paParser;
405     base_options::Options baseOptions("");
406 
407     PandArg<bool> help("help", false, "Print this message and exit");
408     PandArg<bool> options("options", false, "Print verifier options");
409     // tail argument
410     PandArg<std::string> file("file", "", "path to pandafile");
411 
412     cliOptions.AddOptions(&paParser);
413 
414     paParser.Add(&help);
415     paParser.Add(&options);
416     paParser.PushBackTail(&file);
417     paParser.EnableTail();
418 
419     if (!paParser.Parse(argc, argv)) {
420         PrintHelp(paParser);
421         return 1;
422     }
423 
424     if (runtimeOptions.IsVersion()) {
425         PrintPandaVersion();
426         return 0;
427     }
428 
429     if (help.GetValue()) {
430         PrintHelp(paParser);
431         return 0;
432     }
433 
434     if (options.GetValue()) {
435         std::cout << paParser.GetRegularArgs() << std::endl;
436         return 0;
437     }
438 
439     auto cliOptionsErr = cliOptions.Validate();
440     if (cliOptionsErr) {
441         std::cerr << "Error: " << cliOptionsErr.value().GetMessage() << std::endl;
442         return 1;
443     }
444 
445     return Run(cliOptions, runtimeOptions, baseOptions, file);
446 }
447 
448 }  // namespace ark::verifier
449 
450 int main(int argc, const char **argv)
451 {
452     return ark::verifier::Main(argc, argv);
453 }
454