Skip to content

gh-111178: fix UBSan failures in Modules/_zoneinfo.c #129798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 26, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 31 additions & 34 deletions Modules/_zoneinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ typedef struct {
unsigned char source;
} PyZoneInfo_ZoneInfo;

#define PyZoneInfo_ZoneInfo_CAST(op) ((PyZoneInfo_ZoneInfo *)(op))

struct TransitionRuleType {
int64_t (*year_to_timestamp)(TransitionRuleType *, int);
};
Expand Down Expand Up @@ -238,7 +240,7 @@ zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key)
}
}

PyObject *self = (PyObject *)(type->tp_alloc(type, 0));
PyObject *self = type->tp_alloc(type, 0);
if (self == NULL) {
goto error;
}
Expand All @@ -251,7 +253,8 @@ zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key)
}
}

if (load_data(state, (PyZoneInfo_ZoneInfo *)self, file_obj)) {
PyZoneInfo_ZoneInfo *self_zinfo = (PyZoneInfo_ZoneInfo *)self;
if (load_data(state, self_zinfo, file_obj)) {
goto error;
}

Expand All @@ -262,7 +265,7 @@ zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key)
}
Py_DECREF(rv);

((PyZoneInfo_ZoneInfo *)self)->key = Py_NewRef(key);
self_zinfo->key = Py_NewRef(key);

goto cleanup;
error:
Expand Down Expand Up @@ -346,16 +349,18 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
}

static int
zoneinfo_traverse(PyZoneInfo_ZoneInfo *self, visitproc visit, void *arg)
zoneinfo_traverse(PyObject *op, visitproc visit, void *arg)
{
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(op);
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->key);
return 0;
}

static int
zoneinfo_clear(PyZoneInfo_ZoneInfo *self)
zoneinfo_clear(PyObject *op)
{
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(op);
Py_CLEAR(self->key);
Py_CLEAR(self->file_repr);
return 0;
Expand All @@ -364,7 +369,7 @@ zoneinfo_clear(PyZoneInfo_ZoneInfo *self)
static void
zoneinfo_dealloc(PyObject *obj_self)
{
PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(obj_self);
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);

Expand Down Expand Up @@ -395,7 +400,7 @@ zoneinfo_dealloc(PyObject *obj_self)

free_tzrule(&(self->tzrule_after));

zoneinfo_clear(self);
(void)zoneinfo_clear(obj_self);
tp->tp_free(obj_self);
Py_DECREF(tp);
}
Expand All @@ -420,8 +425,7 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls,
PyObject *file_repr = NULL;
PyZoneInfo_ZoneInfo *self = NULL;

PyObject *obj_self = (PyObject *)(type->tp_alloc(type, 0));
self = (PyZoneInfo_ZoneInfo *)obj_self;
self = (PyZoneInfo_ZoneInfo *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
Expand All @@ -439,7 +443,7 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls,
self->source = SOURCE_FILE;
self->file_repr = file_repr;
self->key = Py_NewRef(key);
return obj_self;
return (PyObject *)self;

error:
Py_XDECREF(file_repr);
Expand All @@ -466,7 +470,7 @@ zoneinfo_ZoneInfo_no_cache_impl(PyTypeObject *type, PyTypeObject *cls,
zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
PyObject *out = zoneinfo_new_instance(state, type, key);
if (out != NULL) {
((PyZoneInfo_ZoneInfo *)out)->source = SOURCE_NOCACHE;
PyZoneInfo_ZoneInfo_CAST(out)->source = SOURCE_NOCACHE;
}

return out;
Expand Down Expand Up @@ -558,7 +562,7 @@ zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls,
/*[clinic end generated code: output=b71016c319ba1f91 input=2bb6c5364938f19c]*/
{
zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
_ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt);
_ttinfo *tti = find_ttinfo(state, PyZoneInfo_ZoneInfo_CAST(self), dt);
if (tti == NULL) {
return NULL;
}
Expand All @@ -580,7 +584,7 @@ zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt)
/*[clinic end generated code: output=cb6168d7723a6ae6 input=2167fb80cf8645c6]*/
{
zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
_ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt);
_ttinfo *tti = find_ttinfo(state, PyZoneInfo_ZoneInfo_CAST(self), dt);
if (tti == NULL) {
return NULL;
}
Expand All @@ -603,7 +607,7 @@ zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls,
/*[clinic end generated code: output=3b6ae6c3053ea75a input=15a59a4f92ed1f1f]*/
{
zoneinfo_state *state = zoneinfo_get_state_by_cls(cls);
_ttinfo *tti = find_ttinfo(state, (PyZoneInfo_ZoneInfo *)self, dt);
_ttinfo *tti = find_ttinfo(state, PyZoneInfo_ZoneInfo_CAST(self), dt);
if (tti == NULL) {
return NULL;
}
Expand All @@ -627,7 +631,7 @@ zoneinfo_fromutc(PyObject *obj_self, PyObject *dt)
return NULL;
}

PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(obj_self);

int64_t timestamp;
if (get_local_timestamp(dt, &timestamp)) {
Expand Down Expand Up @@ -736,31 +740,24 @@ zoneinfo_fromutc(PyObject *obj_self, PyObject *dt)
}

static PyObject *
zoneinfo_repr(PyZoneInfo_ZoneInfo *self)
zoneinfo_repr(PyObject *op)
{
PyObject *rv = NULL;
const char *type_name = Py_TYPE((PyObject *)self)->tp_name;
if (!(self->key == Py_None)) {
rv = PyUnicode_FromFormat("%s(key=%R)", type_name, self->key);
}
else {
assert(PyUnicode_Check(self->file_repr));
rv = PyUnicode_FromFormat("%s.from_file(%U)", type_name,
self->file_repr);
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(op);
if (self->key != Py_None) {
return PyUnicode_FromFormat("%T(key=%R)", self, self->key);
}

return rv;
assert(PyUnicode_Check(self->file_repr));
return PyUnicode_FromFormat("%T.from_file(%U)", self, self->file_repr);
}

static PyObject *
zoneinfo_str(PyZoneInfo_ZoneInfo *self)
zoneinfo_str(PyObject *op)
{
if (!(self->key == Py_None)) {
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(op);
if (self->key != Py_None) {
return Py_NewRef(self->key);
}
else {
return zoneinfo_repr(self);
}
return zoneinfo_repr(op);
}

/* Pickles the ZoneInfo object by key and source.
Expand All @@ -776,9 +773,9 @@ zoneinfo_str(PyZoneInfo_ZoneInfo *self)
* Objects constructed from ZoneInfo.from_file cannot be pickled.
*/
static PyObject *
zoneinfo_reduce(PyObject *obj_self, PyObject *unused)
zoneinfo_reduce(PyObject *obj_self, PyObject *Py_UNUSED(dummy))
{
PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
PyZoneInfo_ZoneInfo *self = PyZoneInfo_ZoneInfo_CAST(obj_self);
if (self->source == SOURCE_FILE) {
// Objects constructed from files cannot be pickled.
PyObject *pickle_error =
Expand Down
Loading