Coverage Report

Created: 2025-07-04 06:49

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