Skip to content

Commit 7394c5e

Browse files
committed
Assert os.stat_result members are in bounds at module creation time
1 parent 93cdcb4 commit 7394c5e

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

Modules/posixmodule.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3307,9 +3307,10 @@ os_lstat_impl(PyObject *module, path_t *path, int dir_fd)
33073307
#ifdef HAVE_STATX
33083308
typedef struct {
33093309
PyObject_HEAD
3310-
struct statx stx;
33113310
double atime_sec, btime_sec, ctime_sec, mtime_sec;
33123311
dev_t rdev, dev;
3312+
/* Assertions in posixmodule_exec rely on struct statx being at the end. */
3313+
struct statx stx;
33133314
} Py_statx_result;
33143315

33153316
#define M(attr, type, offset, doc) \
@@ -3363,7 +3364,6 @@ pystatx_result_get_u32(PyObject *op, void *context) {
33633364
Py_statx_result *self = (Py_statx_result *) op;
33643365
uint16_t offset = (uintptr_t)context;
33653366
uint32_t val;
3366-
assert(offset + sizeof(val) <= sizeof(Py_statx_result));
33673367
memcpy(&val, (void *)self + offset, sizeof(val));
33683368
return PyLong_FromUInt32(val);
33693369
}
@@ -3374,7 +3374,6 @@ pystatx_result_get_nsec(PyObject *op, void *context)
33743374
Py_statx_result *self = (Py_statx_result *) op;
33753375
uint16_t offset = (uintptr_t)context;
33763376
struct statx_timestamp val;
3377-
assert(offset + sizeof(val) <= sizeof(Py_statx_result));
33783377
memcpy(&val, (void *)self + offset, sizeof(val));
33793378
_posixstate *state = PyType_GetModuleState(Py_TYPE(op));
33803379
assert(state != NULL);
@@ -18517,6 +18516,47 @@ posixmodule_exec(PyObject *m)
1851718516
#endif
1851818517

1851918518
#ifdef HAVE_STATX
18519+
#ifndef NDEBUG
18520+
/* struct statx may be extended in the future. Assert that our definition
18521+
of struct statx is large enough for all the members we expose to Python.
18522+
These asserts rely on struct statx being the last member of
18523+
Py_statx_result. If you hit these asserts, upgrade your kernel
18524+
userspace API and/or libc headers. */
18525+
for (const PyMemberDef *m = pystatx_result_members; m->name != NULL; ++m) {
18526+
Py_ssize_t size;
18527+
switch (m->type) {
18528+
case Py_T_USHORT:
18529+
size = 2;
18530+
break;
18531+
case Py_T_UINT:
18532+
size = 4;
18533+
break;
18534+
case Py_T_ULONGLONG:
18535+
case Py_T_DOUBLE:
18536+
size = 8;
18537+
break;
18538+
default:
18539+
assert(false);
18540+
}
18541+
assert(m->offset + size <= (Py_ssize_t)sizeof(Py_statx_result));
18542+
}
18543+
18544+
for (const PyGetSetDef *m = pystatx_result_getset; m->name != NULL; ++m) {
18545+
uint16_t offset = (uintptr_t)m->closure;
18546+
Py_ssize_t size;
18547+
if (m->get == pystatx_result_get_u32) {
18548+
size = 4;
18549+
}
18550+
else if (m->get == pystatx_result_get_nsec) {
18551+
size = sizeof(struct statx_timestamp);
18552+
}
18553+
else {
18554+
assert(false);
18555+
}
18556+
assert(offset + size <= (Py_ssize_t)sizeof(Py_statx_result));
18557+
}
18558+
#endif /* !NDEBUG */
18559+
1852018560
if (statx == NULL) {
1852118561
PyObject* dct = PyModule_GetDict(m);
1852218562
if (dct == NULL) {

0 commit comments

Comments
 (0)