2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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 +----------------------------------------------------------------------+
19 #include "hphp/runtime/vm/verifier/check.h"
20 #include "hphp/runtime/vm/verifier/cfg.h"
21 #include "hphp/runtime/vm/verifier/util.h"
22 #include "hphp/runtime/vm/verifier/pretty.h"
29 UnitChecker(const Unit
*, bool verbose
);
34 template<class T
> bool checkLiteral(size_t, const T
*, const char*);
37 bool checkSourceLocs();
38 bool checkPreClasses();
44 template<class... Args
>
45 void error(const char* const fmt
, Args
&&... args
) {
46 verify_error(m_unit
, nullptr, fmt
, std::forward
<Args
>(args
)...);
54 bool checkUnit(const Unit
* unit
, bool verbose
) {
56 printf("verifying unit from %s\n", unit
->filepath()->data());
58 return UnitChecker(unit
, verbose
).verify();
61 // Unit contents to check:
66 // o UnitSourceLoc table
70 UnitChecker::UnitChecker(const Unit
* unit
, bool verbose
)
71 : m_unit(unit
), m_verbose(verbose
) {
74 bool UnitChecker::verify() {
75 return checkStrings() &&
77 //checkSourceLocs() &&
78 //checkPreClasses() &&
84 template<class LitType
>
85 bool UnitChecker::checkLiteral(size_t id
,
90 error("null %s id %zu in unit %s\n", what
, id
,
91 m_unit
->md5().toString().c_str());
94 if (!lt
->isStatic()) {
95 error("non-static %s id %zu in unit %s\n", what
, id
,
96 m_unit
->md5().toString().c_str());
102 bool UnitChecker::checkStrings() {
104 for (size_t i
= 0, n
= m_unit
->numLitstrs(); i
< n
; ++i
) {
105 ok
&= checkLiteral(i
, m_unit
->lookupLitstrId(i
), "string");
109 // * Any string in repo can be null. repo litstrId is checked on load
110 // then discarded. Each Litstr entry becomes a static string.
111 // Strings are hash-commoned so presumably only one can be null per unit.
112 // string_data_hash and string_data_same both crash/assert on null.
113 // * If DB has dups then UnitEmitter commons them - spec should outlaw
114 // dups because UE will assert if db has dups with different ids.
115 // StringData statically keeps a map of loaded static strings
116 // * UE keeps a (String->id) mapping and assigns dups the same id, plus
117 // a table of litstrs indexed by id. Unit stores them as
118 // m_namedInfo, a vector<StringData,NamedEntity=null> of pairs.
119 // * are null characters allowed inside the string?
120 // * are strings utf8-encoded?
123 bool UnitChecker::checkArrays() {
125 for (size_t i
= 0, n
= m_unit
->numArrays(); i
< n
; ++i
) {
126 ok
&= checkLiteral(i
, m_unit
->lookupArrayId(i
), "array");
132 * Check that every byte in the unit's bytecode is inside exactly one
133 * function's code region.
135 bool UnitChecker::checkBytecode() {
137 typedef std::map
<Offset
, const Func
*> FuncMap
; // ordered!
139 m_unit
->forEachFunc([&](const Func
* f
) {
140 if (f
->past() <= f
->base()) {
141 if (!f
->isAbstract() || f
->past() < f
->base()) {
142 error("func size <= 0 [%d:%d] in unit %s\n",
143 f
->base(), f
->past(), m_unit
->md5().toString().c_str());
148 if (f
->base() < 0 || f
->past() > m_unit
->bclen()) {
149 error("function region [%d:%d] out of unit %s bounds [%d:%d]\n",
150 f
->base(), f
->past(), m_unit
->md5().toString().c_str(),
155 if (funcs
.find(f
->base()) != funcs
.end()) {
156 error("duplicate function-base at %d in unit %s\n",
157 f
->base(), m_unit
->md5().toString().c_str());
161 funcs
.insert(FuncMap::value_type(f
->base(), f
));
163 // iterate funcs in offset order, checking for holes and overlap
165 error("unit %s must have at least one func\n",
166 m_unit
->md5().toString().c_str());
169 Offset last_past
= 0;
170 for (FuncMap::iterator i
= funcs
.begin(), e
= funcs
.end(); i
!= e
; ) {
171 const Func
* f
= (*i
).second
; ++i
;
172 if (f
->base() < last_past
) {
173 error("function overlap [%d:%d] in unit %s\n",
174 f
->base(), last_past
, m_unit
->md5().toString().c_str());
176 } else if (f
->base() > last_past
) {
177 error("dead bytecode space [%d:%d] in unit %s\n",
178 last_past
, f
->base(), m_unit
->md5().toString().c_str());
181 last_past
= f
->past();
182 if (i
== e
&& last_past
!= m_unit
->bclen()) {
183 error("dead bytecode [%d:%d] at end of unit %s\n",
184 last_past
, m_unit
->bclen(), m_unit
->md5().toString().c_str());
190 // 1. Bytecode regions for every function must not overlap and must exactly
191 // divide up the bytecode of the whole unit.
192 // 2. Its not an error for an abstract function to have zero size.
195 bool UnitChecker::checkFuncs() {
196 const Func
* pseudo
= nullptr;
200 m_unit
->forEachFunc([&](const Func
* func
) {
201 if (func
->isPseudoMain()) {
204 error("%s", "unit should have exactly one pseudo-main\n");
210 if (func
->attrs() & AttrNative
) {
211 ok
&= checkNativeFunc(func
, m_verbose
);
214 ok
&= checkFunc(func
, m_verbose
);
217 if (!multi
&& m_unit
->getMain() != pseudo
) {
218 error("%s", "funcs and unit disagree on what is the pseudo-main\n");