1 /*
2  * Copyright (c) 2009-2021, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of Google LLC nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "python/protobuf.h"
29 
30 #include "python/descriptor.h"
31 #include "python/descriptor_containers.h"
32 #include "python/descriptor_pool.h"
33 #include "python/extension_dict.h"
34 #include "python/map.h"
35 #include "python/message.h"
36 #include "python/repeated.h"
37 #include "python/unknown_fields.h"
38 
PyUpb_ModuleDealloc(void * module)39 static void PyUpb_ModuleDealloc(void* module) {
40   PyUpb_ModuleState* s = PyModule_GetState(module);
41   PyUpb_WeakMap_Free(s->obj_cache);
42   if (s->c_descriptor_symtab) {
43     upb_DefPool_Free(s->c_descriptor_symtab);
44   }
45 }
46 
PyUpb_SetAllowOversizeProtos(PyObject * m,PyObject * arg)47 PyObject* PyUpb_SetAllowOversizeProtos(PyObject* m, PyObject* arg) {
48   if (!arg || !PyBool_Check(arg)) {
49     PyErr_SetString(PyExc_TypeError,
50                     "Argument to SetAllowOversizeProtos must be boolean");
51     return NULL;
52   }
53   PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
54   state->allow_oversize_protos = PyObject_IsTrue(arg);
55   Py_INCREF(arg);
56   return arg;
57 }
58 
59 static PyMethodDef PyUpb_ModuleMethods[] = {
60     {"SetAllowOversizeProtos", PyUpb_SetAllowOversizeProtos, METH_O,
61      "Enable/disable oversize proto parsing."},
62     {NULL, NULL}};
63 
64 static struct PyModuleDef module_def = {PyModuleDef_HEAD_INIT,
65                                         PYUPB_MODULE_NAME,
66                                         "Protobuf Module",
67                                         sizeof(PyUpb_ModuleState),
68                                         PyUpb_ModuleMethods,  // m_methods
69                                         NULL,                 // m_slots
70                                         NULL,                 // m_traverse
71                                         NULL,                 // m_clear
72                                         PyUpb_ModuleDealloc};
73 
74 // -----------------------------------------------------------------------------
75 // ModuleState
76 // -----------------------------------------------------------------------------
77 
PyUpb_ModuleState_MaybeGet(void)78 PyUpb_ModuleState* PyUpb_ModuleState_MaybeGet(void) {
79   PyObject* module = PyState_FindModule(&module_def);
80   return module ? PyModule_GetState(module) : NULL;
81 }
82 
PyUpb_ModuleState_GetFromModule(PyObject * module)83 PyUpb_ModuleState* PyUpb_ModuleState_GetFromModule(PyObject* module) {
84   PyUpb_ModuleState* state = PyModule_GetState(module);
85   assert(state);
86   assert(PyModule_GetDef(module) == &module_def);
87   return state;
88 }
89 
PyUpb_ModuleState_Get(void)90 PyUpb_ModuleState* PyUpb_ModuleState_Get(void) {
91   PyObject* module = PyState_FindModule(&module_def);
92   assert(module);
93   return PyUpb_ModuleState_GetFromModule(module);
94 }
95 
PyUpb_GetWktBases(PyUpb_ModuleState * state)96 PyObject* PyUpb_GetWktBases(PyUpb_ModuleState* state) {
97   if (!state->wkt_bases) {
98     PyObject* wkt_module = PyImport_ImportModule(PYUPB_PROTOBUF_INTERNAL_PACKAGE
99                                                  ".well_known_types");
100 
101     if (wkt_module == NULL) {
102       return false;
103     }
104 
105     state->wkt_bases = PyObject_GetAttrString(wkt_module, "WKTBASES");
106     PyObject* m = PyState_FindModule(&module_def);
107     // Reparent ownership to m.
108     PyModule_AddObject(m, "__internal_wktbases", state->wkt_bases);
109     Py_DECREF(wkt_module);
110   }
111 
112   return state->wkt_bases;
113 }
114 
115 // -----------------------------------------------------------------------------
116 // WeakMap
117 // -----------------------------------------------------------------------------
118 
119 struct PyUpb_WeakMap {
120   upb_inttable table;
121   upb_Arena* arena;
122 };
123 
PyUpb_WeakMap_New(void)124 PyUpb_WeakMap* PyUpb_WeakMap_New(void) {
125   upb_Arena* arena = upb_Arena_New();
126   PyUpb_WeakMap* map = upb_Arena_Malloc(arena, sizeof(*map));
127   map->arena = arena;
128   upb_inttable_init(&map->table, map->arena);
129   return map;
130 }
131 
PyUpb_WeakMap_Free(PyUpb_WeakMap * map)132 void PyUpb_WeakMap_Free(PyUpb_WeakMap* map) { upb_Arena_Free(map->arena); }
133 
134 // To give better entropy in the table key, we shift away low bits that are
135 // always zero.
136 static const int PyUpb_PtrShift = (sizeof(void*) == 4) ? 2 : 3;
137 
PyUpb_WeakMap_GetKey(const void * key)138 uintptr_t PyUpb_WeakMap_GetKey(const void* key) {
139   uintptr_t n = (uintptr_t)key;
140   assert((n & ((1 << PyUpb_PtrShift) - 1)) == 0);
141   return n >> PyUpb_PtrShift;
142 }
143 
PyUpb_WeakMap_Add(PyUpb_WeakMap * map,const void * key,PyObject * py_obj)144 void PyUpb_WeakMap_Add(PyUpb_WeakMap* map, const void* key, PyObject* py_obj) {
145   upb_inttable_insert(&map->table, PyUpb_WeakMap_GetKey(key),
146                       upb_value_ptr(py_obj), map->arena);
147 }
148 
PyUpb_WeakMap_Delete(PyUpb_WeakMap * map,const void * key)149 void PyUpb_WeakMap_Delete(PyUpb_WeakMap* map, const void* key) {
150   upb_value val;
151   bool removed =
152       upb_inttable_remove(&map->table, PyUpb_WeakMap_GetKey(key), &val);
153   (void)removed;
154   assert(removed);
155 }
156 
PyUpb_WeakMap_TryDelete(PyUpb_WeakMap * map,const void * key)157 void PyUpb_WeakMap_TryDelete(PyUpb_WeakMap* map, const void* key) {
158   upb_inttable_remove(&map->table, PyUpb_WeakMap_GetKey(key), NULL);
159 }
160 
PyUpb_WeakMap_Get(PyUpb_WeakMap * map,const void * key)161 PyObject* PyUpb_WeakMap_Get(PyUpb_WeakMap* map, const void* key) {
162   upb_value val;
163   if (upb_inttable_lookup(&map->table, PyUpb_WeakMap_GetKey(key), &val)) {
164     PyObject* ret = upb_value_getptr(val);
165     Py_INCREF(ret);
166     return ret;
167   } else {
168     return NULL;
169   }
170 }
171 
PyUpb_WeakMap_Next(PyUpb_WeakMap * map,const void ** key,PyObject ** obj,intptr_t * iter)172 bool PyUpb_WeakMap_Next(PyUpb_WeakMap* map, const void** key, PyObject** obj,
173                         intptr_t* iter) {
174   uintptr_t u_key;
175   upb_value val;
176   if (!upb_inttable_next(&map->table, &u_key, &val, iter)) return false;
177   *key = (void*)(u_key << PyUpb_PtrShift);
178   *obj = upb_value_getptr(val);
179   return true;
180 }
181 
PyUpb_WeakMap_DeleteIter(PyUpb_WeakMap * map,intptr_t * iter)182 void PyUpb_WeakMap_DeleteIter(PyUpb_WeakMap* map, intptr_t* iter) {
183   upb_inttable_removeiter(&map->table, iter);
184 }
185 
186 // -----------------------------------------------------------------------------
187 // ObjCache
188 // -----------------------------------------------------------------------------
189 
PyUpb_ObjCache_Instance(void)190 PyUpb_WeakMap* PyUpb_ObjCache_Instance(void) {
191   PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
192   return state->obj_cache;
193 }
194 
PyUpb_ObjCache_Add(const void * key,PyObject * py_obj)195 void PyUpb_ObjCache_Add(const void* key, PyObject* py_obj) {
196   PyUpb_WeakMap_Add(PyUpb_ObjCache_Instance(), key, py_obj);
197 }
198 
PyUpb_ObjCache_Delete(const void * key)199 void PyUpb_ObjCache_Delete(const void* key) {
200   PyUpb_ModuleState* state = PyUpb_ModuleState_MaybeGet();
201   if (!state) {
202     // During the shutdown sequence, our object's Dealloc() methods can be
203     // called *after* our module Dealloc() method has been called.  At that
204     // point our state will be NULL and there is nothing to delete out of the
205     // map.
206     return;
207   }
208   PyUpb_WeakMap_Delete(state->obj_cache, key);
209 }
210 
PyUpb_ObjCache_Get(const void * key)211 PyObject* PyUpb_ObjCache_Get(const void* key) {
212   return PyUpb_WeakMap_Get(PyUpb_ObjCache_Instance(), key);
213 }
214 
215 // -----------------------------------------------------------------------------
216 // Arena
217 // -----------------------------------------------------------------------------
218 
219 typedef struct {
220   PyObject_HEAD;
221   upb_Arena* arena;
222 } PyUpb_Arena;
223 
PyUpb_Arena_New(void)224 PyObject* PyUpb_Arena_New(void) {
225   PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
226   PyUpb_Arena* arena = (void*)PyType_GenericAlloc(state->arena_type, 0);
227   arena->arena = upb_Arena_New();
228   return &arena->ob_base;
229 }
230 
PyUpb_Arena_Dealloc(PyObject * self)231 static void PyUpb_Arena_Dealloc(PyObject* self) {
232   upb_Arena_Free(PyUpb_Arena_Get(self));
233   PyUpb_Dealloc(self);
234 }
235 
PyUpb_Arena_Get(PyObject * arena)236 upb_Arena* PyUpb_Arena_Get(PyObject* arena) {
237   return ((PyUpb_Arena*)arena)->arena;
238 }
239 
240 static PyType_Slot PyUpb_Arena_Slots[] = {
241     {Py_tp_dealloc, PyUpb_Arena_Dealloc},
242     {0, NULL},
243 };
244 
245 static PyType_Spec PyUpb_Arena_Spec = {
246     PYUPB_MODULE_NAME ".Arena",
247     sizeof(PyUpb_Arena),
248     0,  // itemsize
249     Py_TPFLAGS_DEFAULT,
250     PyUpb_Arena_Slots,
251 };
252 
PyUpb_InitArena(PyObject * m)253 static bool PyUpb_InitArena(PyObject* m) {
254   PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m);
255   state->arena_type = PyUpb_AddClass(m, &PyUpb_Arena_Spec);
256   return state->arena_type;
257 }
258 
259 // -----------------------------------------------------------------------------
260 // Utilities
261 // -----------------------------------------------------------------------------
262 
AddObject(PyObject * m,const char * name,PyType_Spec * spec)263 PyTypeObject* AddObject(PyObject* m, const char* name, PyType_Spec* spec) {
264   PyObject* type = PyType_FromSpec(spec);
265   return type && PyModule_AddObject(m, name, type) == 0 ? (PyTypeObject*)type
266                                                         : NULL;
267 }
268 
PyUpb_GetClassName(PyType_Spec * spec)269 static const char* PyUpb_GetClassName(PyType_Spec* spec) {
270   // spec->name contains a fully-qualified name, like:
271   //   google.protobuf.pyext._message.FooBar
272   //
273   // Find the rightmost '.' to get "FooBar".
274   const char* name = strrchr(spec->name, '.');
275   assert(name);
276   return name + 1;
277 }
278 
PyUpb_AddClass(PyObject * m,PyType_Spec * spec)279 PyTypeObject* PyUpb_AddClass(PyObject* m, PyType_Spec* spec) {
280   PyObject* type = PyType_FromSpec(spec);
281   const char* name = PyUpb_GetClassName(spec);
282   if (PyModule_AddObject(m, name, type) < 0) {
283     Py_XDECREF(type);
284     return NULL;
285   }
286   return (PyTypeObject*)type;
287 }
288 
PyUpb_AddClassWithBases(PyObject * m,PyType_Spec * spec,PyObject * bases)289 PyTypeObject* PyUpb_AddClassWithBases(PyObject* m, PyType_Spec* spec,
290                                       PyObject* bases) {
291   PyObject* type = PyType_FromSpecWithBases(spec, bases);
292   const char* name = PyUpb_GetClassName(spec);
293   if (PyModule_AddObject(m, name, type) < 0) {
294     Py_XDECREF(type);
295     return NULL;
296   }
297   return (PyTypeObject*)type;
298 }
299 
PyUpb_GetStrData(PyObject * obj)300 const char* PyUpb_GetStrData(PyObject* obj) {
301   if (PyUnicode_Check(obj)) {
302     return PyUnicode_AsUTF8AndSize(obj, NULL);
303   } else if (PyBytes_Check(obj)) {
304     return PyBytes_AsString(obj);
305   } else {
306     return NULL;
307   }
308 }
309 
PyUpb_VerifyStrData(PyObject * obj)310 const char* PyUpb_VerifyStrData(PyObject* obj) {
311   const char* ret = PyUpb_GetStrData(obj);
312   if (ret) return ret;
313   PyErr_Format(PyExc_TypeError, "Expected string: %S", obj);
314   return NULL;
315 }
316 
PyUpb_Forbidden_New(PyObject * cls,PyObject * args,PyObject * kwds)317 PyObject* PyUpb_Forbidden_New(PyObject* cls, PyObject* args, PyObject* kwds) {
318   PyObject* name = PyObject_GetAttrString(cls, "__name__");
319   PyErr_Format(PyExc_RuntimeError,
320                "Objects of type %U may not be created directly.", name);
321   Py_XDECREF(name);
322   return NULL;
323 }
324 
325 // -----------------------------------------------------------------------------
326 // Module Entry Point
327 // -----------------------------------------------------------------------------
328 
PyInit__message(void)329 __attribute__((visibility("default"))) PyMODINIT_FUNC PyInit__message(void) {
330   PyObject* m = PyModule_Create(&module_def);
331   if (!m) return NULL;
332 
333   PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m);
334 
335   state->allow_oversize_protos = false;
336   state->wkt_bases = NULL;
337   state->obj_cache = PyUpb_WeakMap_New();
338   state->c_descriptor_symtab = NULL;
339 
340   if (!PyUpb_InitDescriptorContainers(m) || !PyUpb_InitDescriptorPool(m) ||
341       !PyUpb_InitDescriptor(m) || !PyUpb_InitArena(m) ||
342       !PyUpb_InitExtensionDict(m) || !PyUpb_Map_Init(m) ||
343       !PyUpb_InitMessage(m) || !PyUpb_Repeated_Init(m) ||
344       !PyUpb_UnknownFields_Init(m)) {
345     Py_DECREF(m);
346     return NULL;
347   }
348 
349   // Temporary: an cookie we can use in the tests to ensure we are testing upb
350   // and not another protobuf library on the system.
351   PyModule_AddIntConstant(m, "_IS_UPB", 1);
352 
353   return m;
354 }
355