Skip to content

Commit c2ee586

Browse files
authored
[librt] Add librt.internal API versioning with backward compat (#20221)
This lets us add new API features by adding more functions to the capsule and the module namespace without breaking backward compatibility. We shouldn't use the capsule if the installed version is too old, since we could be calling functions via uninitialized pointers. Note that this is a breaking change in the `librt.internal` ABI.
1 parent 7c36b24 commit c2ee586

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

mypyc/lib-rt/librt_internal.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,11 @@ NativeInternal_ABI_Version(void) {
962962
return LIBRT_INTERNAL_ABI_VERSION;
963963
}
964964

965+
static int
966+
NativeInternal_API_Version(void) {
967+
return LIBRT_INTERNAL_API_VERSION;
968+
}
969+
965970
static int
966971
librt_internal_module_exec(PyObject *m)
967972
{
@@ -999,6 +1004,7 @@ librt_internal_module_exec(PyObject *m)
9991004
(void *)cache_version_internal,
10001005
(void *)ReadBuffer_type_internal,
10011006
(void *)WriteBuffer_type_internal,
1007+
(void *)NativeInternal_API_Version,
10021008
};
10031009
PyObject *c_api_object = PyCapsule_New((void *)NativeInternal_API, "librt.internal._C_API", NULL);
10041010
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {

mypyc/lib-rt/librt_internal.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
#ifndef LIBRT_INTERNAL_H
22
#define LIBRT_INTERNAL_H
33

4-
#define LIBRT_INTERNAL_ABI_VERSION 1
5-
#define LIBRT_INTERNAL_API_LEN 19
4+
// ABI version -- only an exact match is compatible. This will only be changed in
5+
// very exceptional cases (likely never) due to strict backward compatibility
6+
// requirements.
7+
#define LIBRT_INTERNAL_ABI_VERSION 2
8+
9+
// API version -- more recent versions must maintain backward compatibility, i.e.
10+
// we can add new features but not remove or change existing features (unless
11+
// ABI version is changed, but see the comment above).
12+
#define LIBRT_INTERNAL_API_VERSION 0
13+
14+
// Number of functions in the capsule API. If you add a new function, also increase
15+
// LIBRT_INTERNAL_API_VERSION.
16+
#define LIBRT_INTERNAL_API_LEN 20
617

718
#ifdef LIBRT_INTERNAL_MODULE
819

@@ -27,6 +38,7 @@ static PyObject *read_bytes_internal(PyObject *data);
2738
static uint8_t cache_version_internal(void);
2839
static PyTypeObject *ReadBuffer_type_internal(void);
2940
static PyTypeObject *WriteBuffer_type_internal(void);
41+
static int NativeInternal_API_Version(void);
3042

3143
#else
3244

@@ -51,6 +63,7 @@ static void *NativeInternal_API[LIBRT_INTERNAL_API_LEN];
5163
#define cache_version_internal (*(uint8_t (*)(void)) NativeInternal_API[16])
5264
#define ReadBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[17])
5365
#define WriteBuffer_type_internal (*(PyTypeObject* (*)(void)) NativeInternal_API[18])
66+
#define NativeInternal_API_Version (*(int (*)(void)) NativeInternal_API[19])
5467

5568
static int
5669
import_librt_internal(void)
@@ -72,6 +85,16 @@ import_librt_internal(void)
7285
PyErr_SetString(PyExc_ValueError, err);
7386
return -1;
7487
}
88+
if (NativeInternal_API_Version() < LIBRT_INTERNAL_API_VERSION) {
89+
char err[128];
90+
snprintf(err, sizeof(err),
91+
"API version conflict for librt.internal, expected %d or newer, found %d (hint: upgrade librt)",
92+
LIBRT_INTERNAL_API_VERSION,
93+
NativeInternal_API_Version()
94+
);
95+
PyErr_SetString(PyExc_ValueError, err);
96+
return -1;
97+
}
7598
return 0;
7699
}
77100

0 commit comments

Comments
 (0)