Coverage Report

Created: 2026-02-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Python/structmember.c
Line
Count
Source
1
2
/* Map C struct members to Python object attributes */
3
4
#include "Python.h"
5
#include "pycore_abstract.h"      // _PyNumber_Index()
6
#include "pycore_descrobject.h"   // _PyMember_GetOffset()
7
#include "pycore_long.h"          // _PyLong_IsNegative()
8
#include "pycore_object.h"        // _Py_TryIncrefCompare(), FT_ATOMIC_*()
9
#include "pycore_critical_section.h"
10
11
12
static inline PyObject *
13
member_get_object(const char *addr, const char *obj_addr, PyMemberDef *l)
14
932k
{
15
932k
    PyObject *v = FT_ATOMIC_LOAD_PTR(*(PyObject **) addr);
16
932k
    if (v == NULL) {
17
894k
        PyErr_Format(PyExc_AttributeError,
18
894k
                     "'%T' object has no attribute '%s'",
19
894k
                     (PyObject *)obj_addr, l->name);
20
894k
    }
21
932k
    return v;
22
932k
}
23
24
void *
25
_PyMember_GetOffset(PyObject *obj, PyMemberDef *mp)
26
71.5M
{
27
71.5M
    unsigned char *addr = (unsigned char *)obj + mp->offset;
28
71.5M
    if (mp->flags & _Py_AFTER_ITEMS) {
29
0
        PyTypeObject *type = Py_TYPE(obj);
30
0
        addr += _Py_SIZE_ROUND_UP(Py_SIZE(obj) * type->tp_itemsize, SIZEOF_VOID_P);
31
0
    }
32
71.5M
    return addr;
33
71.5M
}
34
35
PyObject *
36
PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
37
1.26M
{
38
1.26M
    PyObject *v;
39
1.26M
    if (l->flags & Py_RELATIVE_OFFSET) {
40
0
        PyErr_SetString(
41
0
            PyExc_SystemError,
42
0
            "PyMember_GetOne used with Py_RELATIVE_OFFSET");
43
0
        return NULL;
44
0
    }
45
46
1.26M
    const void *addr = _PyMember_GetOffset((PyObject *)obj_addr, l);
47
1.26M
    switch (l->type) {
48
238k
    case Py_T_BOOL:
49
238k
        v = PyBool_FromLong(FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr));
50
238k
        break;
51
0
    case Py_T_BYTE:
52
0
        v = PyLong_FromLong(FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr));
53
0
        break;
54
0
    case Py_T_UBYTE:
55
0
        v = PyLong_FromUnsignedLong(FT_ATOMIC_LOAD_UCHAR_RELAXED(*(unsigned char*)addr));
56
0
        break;
57
0
    case Py_T_SHORT:
58
0
        v = PyLong_FromLong(FT_ATOMIC_LOAD_SHORT_RELAXED(*(short*)addr));
59
0
        break;
60
0
    case Py_T_USHORT:
61
0
        v = PyLong_FromUnsignedLong(FT_ATOMIC_LOAD_USHORT_RELAXED(*(unsigned short*)addr));
62
0
        break;
63
2.23k
    case Py_T_INT:
64
2.23k
        v = PyLong_FromLong(FT_ATOMIC_LOAD_INT_RELAXED(*(int*)addr));
65
2.23k
        break;
66
0
    case Py_T_UINT:
67
0
        v = PyLong_FromUnsignedLong(FT_ATOMIC_LOAD_UINT_RELAXED(*(unsigned int*)addr));
68
0
        break;
69
0
    case Py_T_LONG:
70
0
        v = PyLong_FromLong(FT_ATOMIC_LOAD_LONG_RELAXED(*(long*)addr));
71
0
        break;
72
0
    case Py_T_ULONG:
73
0
        v = PyLong_FromUnsignedLong(FT_ATOMIC_LOAD_ULONG_RELAXED(*(unsigned long*)addr));
74
0
        break;
75
4.94k
    case Py_T_PYSSIZET:
76
4.94k
        v = PyLong_FromSsize_t(FT_ATOMIC_LOAD_SSIZE_RELAXED(*(Py_ssize_t*)addr));
77
4.94k
        break;
78
0
    case Py_T_FLOAT:
79
0
        v = PyFloat_FromDouble((double)FT_ATOMIC_LOAD_FLOAT_RELAXED(*(float*)addr));
80
0
        break;
81
0
    case Py_T_DOUBLE:
82
0
        v = PyFloat_FromDouble(FT_ATOMIC_LOAD_DOUBLE_RELAXED(*(double*)addr));
83
0
        break;
84
2
    case Py_T_STRING:
85
2
        if (*(char**)addr == NULL) {
86
0
            v = Py_NewRef(Py_None);
87
0
        }
88
2
        else
89
2
            v = PyUnicode_FromString(*(char**)addr);
90
2
        break;
91
0
    case Py_T_STRING_INPLACE:
92
0
        v = PyUnicode_FromString((char*)addr);
93
0
        break;
94
0
    case Py_T_CHAR: {
95
0
        char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr);
96
0
        v = PyUnicode_FromStringAndSize(&char_val, 1);
97
0
        break;
98
0
    }
99
91.6k
    case _Py_T_OBJECT:
100
91.6k
        v = FT_ATOMIC_LOAD_PTR(*(PyObject **) addr);
101
91.6k
        if (v != NULL) {
102
#ifdef Py_GIL_DISABLED
103
            if (!_Py_TryIncrefCompare((PyObject **) addr, v)) {
104
                Py_BEGIN_CRITICAL_SECTION((PyObject *) obj_addr);
105
                v = FT_ATOMIC_LOAD_PTR(*(PyObject **) addr);
106
                Py_XINCREF(v);
107
                Py_END_CRITICAL_SECTION();
108
            }
109
#else
110
91.6k
            Py_INCREF(v);
111
91.6k
#endif
112
91.6k
        }
113
91.6k
        if (v == NULL) {
114
24
            v = Py_None;
115
24
        }
116
91.6k
        break;
117
932k
    case Py_T_OBJECT_EX:
118
932k
        v = member_get_object(addr, obj_addr, l);
119
932k
#ifndef Py_GIL_DISABLED
120
932k
        Py_XINCREF(v);
121
#else
122
        if (v != NULL) {
123
            if (!_Py_TryIncrefCompare((PyObject **) addr, v)) {
124
                Py_BEGIN_CRITICAL_SECTION((PyObject *) obj_addr);
125
                v = member_get_object(addr, obj_addr, l);
126
                Py_XINCREF(v);
127
                Py_END_CRITICAL_SECTION();
128
            }
129
        }
130
#endif
131
932k
        break;
132
0
    case Py_T_LONGLONG:
133
0
        v = PyLong_FromLongLong(FT_ATOMIC_LOAD_LLONG_RELAXED(*(long long *)addr));
134
0
        break;
135
0
    case Py_T_ULONGLONG:
136
0
        v = PyLong_FromUnsignedLongLong(FT_ATOMIC_LOAD_ULLONG_RELAXED(*(unsigned long long *)addr));
137
0
        break;
138
0
    case _Py_T_NONE:
139
        // doesn't require free-threading code path
140
0
        v = Py_NewRef(Py_None);
141
0
        break;
142
0
    default:
143
0
        PyErr_SetString(PyExc_SystemError, "bad memberdescr type");
144
0
        v = NULL;
145
1.26M
    }
146
1.26M
    return v;
147
1.26M
}
148
149
#define WARN(msg)                                               \
150
0
    do {                                                        \
151
0
    if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 1) < 0)         \
152
0
        return -1;                                              \
153
0
    } while (0)
154
155
int
156
PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
157
1.93M
{
158
1.93M
    PyObject *oldv;
159
1.93M
    if (l->flags & Py_RELATIVE_OFFSET) {
160
0
        PyErr_SetString(
161
0
            PyExc_SystemError,
162
0
            "PyMember_SetOne used with Py_RELATIVE_OFFSET");
163
0
        return -1;
164
0
    }
165
166
1.93M
    PyObject *obj = (PyObject *)addr;
167
1.93M
    addr = _PyMember_GetOffset(obj, l);
168
169
1.93M
    if ((l->flags & Py_READONLY))
170
0
    {
171
0
        PyErr_SetString(PyExc_AttributeError, "readonly attribute");
172
0
        return -1;
173
0
    }
174
1.93M
    if (v == NULL) {
175
0
        if (l->type == Py_T_OBJECT_EX) {
176
            /* Check if the attribute is set. */
177
0
            if (*(PyObject **)addr == NULL) {
178
0
                PyErr_SetString(PyExc_AttributeError, l->name);
179
0
                return -1;
180
0
            }
181
0
        }
182
0
        else if (l->type != _Py_T_OBJECT) {
183
0
            PyErr_SetString(PyExc_TypeError,
184
0
                            "can't delete numeric/char attribute");
185
0
            return -1;
186
0
        }
187
0
    }
188
1.93M
    switch (l->type) {
189
3
    case Py_T_BOOL:{
190
3
        if (!PyBool_Check(v)) {
191
0
            PyErr_SetString(PyExc_TypeError,
192
0
                            "attribute value type must be bool");
193
0
            return -1;
194
0
        }
195
3
        if (v == Py_True)
196
3
            FT_ATOMIC_STORE_CHAR_RELAXED(*(char*)addr, 1);
197
0
        else
198
0
            FT_ATOMIC_STORE_CHAR_RELAXED(*(char*)addr, 0);
199
3
        break;
200
3
        }
201
0
    case Py_T_BYTE:{
202
0
        long long_val = PyLong_AsLong(v);
203
0
        if ((long_val == -1) && PyErr_Occurred())
204
0
            return -1;
205
0
        FT_ATOMIC_STORE_CHAR_RELAXED(*(char*)addr, (char)long_val);
206
        /* XXX: For compatibility, only warn about truncations
207
           for now. */
208
0
        if ((long_val > CHAR_MAX) || (long_val < CHAR_MIN))
209
0
            WARN("Truncation of value to char");
210
0
        break;
211
0
        }
212
0
    case Py_T_UBYTE:{
213
0
        long long_val = PyLong_AsLong(v);
214
0
        if ((long_val == -1) && PyErr_Occurred())
215
0
            return -1;
216
0
        FT_ATOMIC_STORE_UCHAR_RELAXED(*(unsigned char*)addr, (unsigned char)long_val);
217
0
        if ((long_val > UCHAR_MAX) || (long_val < 0))
218
0
            WARN("Truncation of value to unsigned char");
219
0
        break;
220
0
        }
221
0
    case Py_T_SHORT:{
222
0
        long long_val = PyLong_AsLong(v);
223
0
        if ((long_val == -1) && PyErr_Occurred())
224
0
            return -1;
225
0
        FT_ATOMIC_STORE_SHORT_RELAXED(*(short*)addr, (short)long_val);
226
0
        if ((long_val > SHRT_MAX) || (long_val < SHRT_MIN))
227
0
            WARN("Truncation of value to short");
228
0
        break;
229
0
        }
230
0
    case Py_T_USHORT:{
231
0
        long long_val = PyLong_AsLong(v);
232
0
        if ((long_val == -1) && PyErr_Occurred())
233
0
            return -1;
234
0
        FT_ATOMIC_STORE_USHORT_RELAXED(*(unsigned short*)addr, (unsigned short)long_val);
235
0
        if ((long_val > USHRT_MAX) || (long_val < 0))
236
0
            WARN("Truncation of value to unsigned short");
237
0
        break;
238
0
        }
239
0
    case Py_T_INT:{
240
0
        long long_val = PyLong_AsLong(v);
241
0
        if ((long_val == -1) && PyErr_Occurred())
242
0
            return -1;
243
0
        FT_ATOMIC_STORE_INT_RELAXED(*(int *)addr, (int)long_val);
244
0
        if ((long_val > INT_MAX) || (long_val < INT_MIN))
245
0
            WARN("Truncation of value to int");
246
0
        break;
247
0
        }
248
0
    case Py_T_UINT: {
249
        /* XXX: For compatibility, accept negative int values
250
           as well. */
251
0
        v = _PyNumber_Index(v);
252
0
        if (v == NULL) {
253
0
            return -1;
254
0
        }
255
0
        if (_PyLong_IsNegative((PyLongObject *)v)) {
256
0
            long long_val = PyLong_AsLong(v);
257
0
            Py_DECREF(v);
258
0
            if (long_val == -1 && PyErr_Occurred()) {
259
0
                return -1;
260
0
            }
261
0
            FT_ATOMIC_STORE_UINT_RELAXED(*(unsigned int *)addr, (unsigned int)(unsigned long)long_val);
262
0
            WARN("Writing negative value into unsigned field");
263
0
        }
264
0
        else {
265
0
            unsigned long ulong_val = PyLong_AsUnsignedLong(v);
266
0
            Py_DECREF(v);
267
0
            if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
268
0
                return -1;
269
0
            }
270
0
            FT_ATOMIC_STORE_UINT_RELAXED(*(unsigned int *)addr, (unsigned int)ulong_val);
271
0
            if (ulong_val > UINT_MAX) {
272
0
                WARN("Truncation of value to unsigned int");
273
0
            }
274
0
        }
275
0
        break;
276
0
    }
277
0
    case Py_T_LONG: {
278
0
        const long long_val = PyLong_AsLong(v);
279
0
        if ((long_val == -1) && PyErr_Occurred())
280
0
            return -1;
281
0
        FT_ATOMIC_STORE_LONG_RELAXED(*(long*)addr, long_val);
282
0
        break;
283
0
    }
284
0
    case Py_T_ULONG: {
285
        /* XXX: For compatibility, accept negative int values
286
           as well. */
287
0
        v = _PyNumber_Index(v);
288
0
        if (v == NULL) {
289
0
            return -1;
290
0
        }
291
0
        if (_PyLong_IsNegative((PyLongObject *)v)) {
292
0
            long long_val = PyLong_AsLong(v);
293
0
            Py_DECREF(v);
294
0
            if (long_val == -1 && PyErr_Occurred()) {
295
0
                return -1;
296
0
            }
297
0
            FT_ATOMIC_STORE_ULONG_RELAXED(*(unsigned long *)addr, (unsigned long)long_val);
298
0
            WARN("Writing negative value into unsigned field");
299
0
        }
300
0
        else {
301
0
            unsigned long ulong_val = PyLong_AsUnsignedLong(v);
302
0
            Py_DECREF(v);
303
0
            if (ulong_val == (unsigned long)-1 && PyErr_Occurred()) {
304
0
                return -1;
305
0
            }
306
0
            FT_ATOMIC_STORE_ULONG_RELAXED(*(unsigned long *)addr, ulong_val);
307
0
        }
308
0
        break;
309
0
    }
310
0
    case Py_T_PYSSIZET: {
311
0
        const Py_ssize_t ssize_val = PyLong_AsSsize_t(v);
312
0
        if ((ssize_val == (Py_ssize_t)-1) && PyErr_Occurred())
313
0
            return -1;
314
0
        FT_ATOMIC_STORE_SSIZE_RELAXED(*(Py_ssize_t*)addr, ssize_val);
315
0
        break;
316
0
    }
317
0
    case Py_T_FLOAT:{
318
0
        double double_val = PyFloat_AsDouble(v);
319
0
        if ((double_val == -1) && PyErr_Occurred())
320
0
            return -1;
321
0
        FT_ATOMIC_STORE_FLOAT_RELAXED(*(float*)addr, (float)double_val);
322
0
        break;
323
0
        }
324
0
    case Py_T_DOUBLE: {
325
0
        const double double_val = PyFloat_AsDouble(v);
326
0
        if ((double_val == -1) && PyErr_Occurred())
327
0
            return -1;
328
0
        FT_ATOMIC_STORE_DOUBLE_RELAXED(*(double *) addr, double_val);
329
0
        break;
330
0
    }
331
1.93M
    case _Py_T_OBJECT:
332
1.93M
    case Py_T_OBJECT_EX:
333
1.93M
        Py_BEGIN_CRITICAL_SECTION(obj);
334
1.93M
        oldv = *(PyObject **)addr;
335
1.93M
        FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, Py_XNewRef(v));
336
1.93M
        Py_END_CRITICAL_SECTION();
337
1.93M
        Py_XDECREF(oldv);
338
1.93M
        break;
339
0
    case Py_T_CHAR: {
340
0
        const char *string;
341
0
        Py_ssize_t len;
342
343
0
        string = PyUnicode_AsUTF8AndSize(v, &len);
344
0
        if (string == NULL || len != 1) {
345
0
            PyErr_BadArgument();
346
0
            return -1;
347
0
        }
348
0
        FT_ATOMIC_STORE_CHAR_RELAXED(*(char*)addr, string[0]);
349
0
        break;
350
0
        }
351
0
    case Py_T_STRING:
352
0
    case Py_T_STRING_INPLACE:
353
0
        PyErr_SetString(PyExc_TypeError, "readonly attribute");
354
0
        return -1;
355
0
    case Py_T_LONGLONG:{
356
0
        long long value = PyLong_AsLongLong(v);
357
0
        if ((value == -1) && PyErr_Occurred())
358
0
            return -1;
359
0
        FT_ATOMIC_STORE_LLONG_RELAXED(*(long long*)addr, value);
360
0
        break;
361
0
        }
362
0
    case Py_T_ULONGLONG: {
363
0
        v = _PyNumber_Index(v);
364
0
        if (v == NULL) {
365
0
            return -1;
366
0
        }
367
0
        if (_PyLong_IsNegative((PyLongObject *)v)) {
368
0
            long long_val = PyLong_AsLong(v);
369
0
            Py_DECREF(v);
370
0
            if (long_val == -1 && PyErr_Occurred()) {
371
0
                return -1;
372
0
            }
373
0
            FT_ATOMIC_STORE_ULLONG_RELAXED(*(unsigned long long *)addr, (unsigned long long)(long long)long_val);
374
0
            WARN("Writing negative value into unsigned field");
375
0
        }
376
0
        else {
377
0
            unsigned long long ulonglong_val = PyLong_AsUnsignedLongLong(v);
378
0
            Py_DECREF(v);
379
0
            if (ulonglong_val == (unsigned long long)-1 && PyErr_Occurred()) {
380
0
                return -1;
381
0
            }
382
0
            FT_ATOMIC_STORE_ULLONG_RELAXED(*(unsigned long long *)addr, ulonglong_val);
383
0
        }
384
0
        break;
385
0
    }
386
0
    default:
387
0
        PyErr_Format(PyExc_SystemError,
388
0
                     "bad memberdescr type for %s", l->name);
389
0
        return -1;
390
1.93M
    }
391
1.93M
    return 0;
392
1.93M
}