This fixes a bug in PHP/HH's crypt_blowfish implementation that can cause a short...
[hiphop-php.git] / hphp / util / type-scan.cpp
blob78c505a3cb24e32dc1415e836bf1f82010026341
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/type-scan.h"
19 #include <cstdio>
20 #include <dlfcn.h>
21 #include <memory>
22 #include <vector>
24 #include <folly/container/F14Set.h>
25 #include <folly/Format.h>
27 #include "hphp/util/build-info.h"
28 #include "hphp/util/embedded-data.h"
30 namespace {
32 ////////////////////////////////////////////////////////////////////////////////
34 // List of types which should be ignored (including any bases) by the generated
35 // scanners. Add as needed.
36 const folly::F14FastSet<std::string> ignored = {
37 "pthread_cond_t",
38 "std::condition_variable",
39 "st_mysql_bind",
42 // List of templates which should not be used to store request heap allocated
43 // values (because scanner aware variants exist).
44 const folly::F14FastSet<std::string> forbidden_template = {
45 "boost::container::flat_map",
46 "boost::container::flat_multimap",
47 "boost::container::flat_multiset",
48 "boost::container::flat_set",
49 "HPHP::FixedVector",
50 "HPHP::TinyVector",
51 "HPHP::Optional",
52 "std::deque",
53 "std::forward_list",
54 "std::list",
55 "std::map",
56 "std::multimap",
57 "std::multiset",
58 "std::priority_queue",
59 "std::queue",
60 "std::set",
61 "std::shared_ptr",
62 "std::stack",
63 "std::unique_ptr",
64 "std::unordered_map",
65 "std::unordered_multimap",
66 "std::unordered_multiset",
67 "std::unordered_set",
68 "std::vector"
71 const folly::F14FastSet<std::string> forced_conservative = {
72 "boost::variant",
73 "boost::Optional",
74 "std::optional",
75 "std::function"
78 std::string stripTemplateArgs(std::string name) {
79 std::size_t open_count = 0;
80 std::size_t open_begin;
81 for (std::size_t i = 0; i < name.size();) {
82 if (name[i] == '<') {
83 if (!open_count) open_begin = i;
84 ++open_count;
85 ++i;
86 } else if (name[i] == '>' && open_count > 0) {
87 if (!--open_count) {
88 name = name.erase(open_begin, i - open_begin + 1);
89 i = open_begin;
90 } else {
91 ++i;
93 } else {
94 ++i;
97 return name;
100 ////////////////////////////////////////////////////////////////////////////////
104 namespace HPHP::type_scan { namespace detail {
106 ////////////////////////////////////////////////////////////////////////////////
108 void conservative_stub(type_scan::Scanner& scanner,
109 const void* ptr,
110 std::size_t size) {
111 scanner.conservative(ptr, size);
114 void noptrs_stub(type_scan::Scanner& /*scanner*/, const void* /*ptr*/,
115 std::size_t /*size*/) {}
117 // Initialize metadata table, used before init() is called. Since before this,
118 // the only type-indices that can be present are "kIndexUnknown" and
119 // "kIndexUnknownNoPtrs".
120 const Metadata stub_metadata_table[] = {
121 {"(UNKNOWN)", conservative_stub},
122 {"(UNKNOWN NO-PTRS)", noptrs_stub}
124 const Metadata* g_metadata_table = stub_metadata_table;
125 std::size_t g_metadata_table_size = 2;
127 bool isIgnoredType(const std::string& name) {
128 return ignored.count(stripTemplateArgs(name));
131 bool isForbiddenTemplate(const std::string& name) {
132 return forbidden_template.count(stripTemplateArgs(name));
135 bool isForcedConservativeTemplate(const std::string& name) {
136 return forced_conservative.count(stripTemplateArgs(name));
139 ////////////////////////////////////////////////////////////////////////////////
143 using namespace detail;
145 void init(const std::string& extractPath,
146 const std::string& fallbackPath,
147 bool trust) {
148 #if defined(__clang__) && !defined(CLANG_STANDALONE_DEBUG)
149 // Older versions of clang don't emit uncalled member functions in a
150 // template class, even when using ATTRIBUTE_USED. This prevents the custom
151 // scanners from being emitted (silently), which causes all sorts of
152 // problems. Fixed in https://reviews.llvm.org/D56928. Clang builds also
153 // need -fstandalone-debug to emit full types in DWARF.
154 return;
155 #elif defined(__linux__) || defined(__FreeBSD__)
157 using init_func_t = const Metadata*(*)(std::size_t&);
159 auto const scanner_init = [&] () -> init_func_t {
160 auto const result = dlsym(RTLD_DEFAULT, kInitFuncName);
161 if (result != nullptr) return reinterpret_cast<init_func_t>(result);
163 // Find the shared object embedded within a custom section.
164 embedded_data data;
165 if (!get_embedded_data("type_scanners", &data)) {
166 // no embedded data was built; fall back to conservative scan.
167 return nullptr;
170 // Link in the embedded object.
171 auto const handle = dlopen_embedded_data(
172 data,
173 extractPath,
174 fallbackPath,
175 buildId().toString(),
176 trust
178 if (!handle) {
179 throw InitException{"Failed to dlopen embedded data"};
182 // Find the initialization function.
183 auto const init = dlsym(handle, kInitFuncName);
184 if (!init) {
185 throw InitException { folly::sformat("dlsym() fails: {}", dlerror()) };
187 return reinterpret_cast<init_func_t>(init);
188 }();
190 if (!scanner_init) return;
192 // And call it. The return value is a pointer to the new metadata table, and
193 // the size of the table will be updated by passing by ref.
194 if (auto const table = scanner_init(g_metadata_table_size)) {
195 g_metadata_table = table;
196 } else {
197 throw InitException{"Failed to load scanner table"};
199 #endif
201 // Some other platform... do nothing and continue to conservative scan for
202 // now.
205 ////////////////////////////////////////////////////////////////////////////////