Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Python/importdl.c
Line
Count
Source
1
2
/* Support for dynamic loading of extension modules */
3
4
#include "Python.h"
5
#include "pycore_call.h"          // _PyObject_CallMethod()
6
#include "pycore_import.h"        // _PyImport_SwapPackageContext()
7
#include "pycore_importdl.h"
8
#include "pycore_moduleobject.h"  // _PyModule_GetDefOrNull()
9
#include "pycore_pyerrors.h"      // _PyErr_FormatFromCause()
10
#include "pycore_runtime.h"       // _Py_ID()
11
#include "pycore_global_objects.h"  // _Py_SINGLETON()
12
13
14
/***********************************/
15
/* module info to use when loading */
16
/***********************************/
17
18
static const struct hook_prefixes ascii_only_prefixes = {
19
    "PyInit", "PyModExport"};
20
static const struct hook_prefixes nonascii_prefixes = {
21
    "PyInitU", "PyModExportU"};
22
23
/* Get the variable part of a module's export symbol name.
24
 * Returns a bytes instance. For non-ASCII-named modules, the name is
25
 * encoded as per PEP 489.
26
 * The hook_prefix pointer is set to either ascii_only_prefix or
27
 * nonascii_prefix, as appropriate.
28
 */
29
static PyObject *
30
956
get_encoded_name(PyObject *name, const struct hook_prefixes **hook_prefixes) {
31
956
    PyObject *tmp;
32
956
    Py_ssize_t name_len, lastdot;
33
34
    /* Get the short name (substring after last dot) */
35
956
    name_len = PyUnicode_GetLength(name);
36
956
    if (name_len < 0) {
37
0
        return NULL;
38
0
    }
39
956
    lastdot = PyUnicode_FindChar(name, '.', 0, name_len, -1);
40
956
    if (lastdot < -1) {
41
0
        return NULL;
42
956
    } else if (lastdot >= 0) {
43
4
        tmp = PyUnicode_Substring(name, lastdot + 1, name_len);
44
4
        if (tmp == NULL)
45
0
            return NULL;
46
4
        name = tmp;
47
        /* "name" now holds a new reference to the substring */
48
952
    } else {
49
952
        Py_INCREF(name);
50
952
    }
51
52
    /* Encode to ASCII or Punycode, as needed */
53
956
    PyObject *encoded;
54
956
    if (PyUnicode_IS_ASCII(name)) {
55
956
        *hook_prefixes = &ascii_only_prefixes;
56
956
        encoded = PyUnicode_AsASCIIString(name);
57
956
    } else {
58
0
        *hook_prefixes = &nonascii_prefixes;
59
0
        encoded = PyUnicode_AsEncodedString(name, "punycode", NULL);
60
0
    }
61
956
    if (encoded == NULL) {
62
0
        goto error;
63
0
    }
64
65
    /* Call encoded.replace('-', '_') */
66
956
    PyObject *modname = PyObject_VectorcallMethod(
67
956
        &_Py_ID(replace),
68
956
        (PyObject*[3]) {
69
956
            encoded,
70
956
            (PyObject *)&_Py_SINGLETON(bytes_characters)['-'],
71
956
            (PyObject *)&_Py_SINGLETON(bytes_characters)['_'],
72
956
        },
73
956
        3 | PY_VECTORCALL_ARGUMENTS_OFFSET,
74
956
        NULL);
75
956
    Py_CLEAR(encoded);
76
77
956
    if (modname == NULL) {
78
0
        goto error;
79
0
    }
80
81
956
    Py_DECREF(name);
82
956
    return modname;
83
0
error:
84
0
    Py_DECREF(name);
85
0
    return NULL;
86
956
}
87
88
void
89
_Py_ext_module_loader_info_clear(struct _Py_ext_module_loader_info *info)
90
956
{
91
956
    Py_CLEAR(info->filename);
92
956
#ifndef MS_WINDOWS
93
956
    Py_CLEAR(info->filename_encoded);
94
956
#endif
95
956
    Py_CLEAR(info->name);
96
956
    Py_CLEAR(info->name_encoded);
97
956
}
98
99
int
100
_Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info,
101
                                PyObject *name,
102
                                PyObject *spec,
103
                                _Py_ext_module_origin origin)
104
956
{
105
956
    struct _Py_ext_module_loader_info info = {
106
956
        .origin=origin,
107
956
    };
108
109
956
    if (name == NULL) {
110
919
        assert(spec != NULL);
111
919
        info.name = PyObject_GetAttr(spec, &_Py_ID(name));
112
919
        if (info.name == NULL) {
113
0
            goto error;
114
0
        }
115
919
    }
116
37
    else {
117
37
        info.name = Py_NewRef(name);
118
37
    }
119
120
956
    if (!PyUnicode_Check(info.name)) {
121
0
        PyErr_Format(PyExc_TypeError,
122
0
                     "module name must be a string, not %T",
123
0
                     info.name);
124
0
        goto error;
125
0
    }
126
127
956
    if (PyUnicode_GetLength(info.name) == 0) {
128
0
        PyErr_Format(PyExc_ValueError, "module name must not be empty");
129
0
        goto error;
130
0
    }
131
132
956
    info.name_encoded = get_encoded_name(info.name, &info.hook_prefixes);
133
956
    if (info.name_encoded == NULL) {
134
0
        goto error;
135
0
    }
136
137
956
    info.newcontext = PyUnicode_AsUTF8(info.name);
138
956
    if (info.newcontext == NULL) {
139
0
        goto error;
140
0
    }
141
142
956
    if (origin == _Py_ext_module_origin_DYNAMIC) {
143
203
        assert(spec != NULL);
144
203
        info.filename = PyObject_GetAttr(spec, &_Py_ID(origin));
145
203
        if (info.filename == NULL) {
146
0
            goto error;
147
0
        }
148
149
203
        if (!PyUnicode_Check(info.filename)) {
150
0
            PyErr_SetString(PyExc_TypeError,
151
0
                            "module filename must be a string");
152
0
            goto error;
153
0
        }
154
155
203
#ifndef MS_WINDOWS
156
203
        info.filename_encoded = PyUnicode_EncodeFSDefault(info.filename);
157
203
        if (info.filename_encoded == NULL) {
158
0
            goto error;
159
0
        }
160
203
#endif
161
162
203
        info.path = info.filename;
163
203
    }
164
753
    else {
165
753
        info.filename = NULL;
166
753
        info.path = info.name;
167
753
    }
168
169
956
    *p_info = info;
170
956
    return 0;
171
172
0
error:
173
0
    _Py_ext_module_loader_info_clear(&info);
174
0
    return -1;
175
956
}
176
177
178
/********************************/
179
/* module init function results */
180
/********************************/
181
182
void
183
_Py_ext_module_loader_result_clear(struct _Py_ext_module_loader_result *res)
184
952
{
185
    /* Instead, the caller should have called
186
     * _Py_ext_module_loader_result_apply_error(). */
187
952
    assert(res->err == NULL);
188
952
    *res = (struct _Py_ext_module_loader_result){0};
189
952
}
190
191
static void
192
_Py_ext_module_loader_result_set_error(
193
                            struct _Py_ext_module_loader_result *res,
194
                            enum _Py_ext_module_loader_result_error_kind kind)
195
0
{
196
#ifndef NDEBUG
197
    switch (kind) {
198
    case _Py_ext_module_loader_result_EXCEPTION: _Py_FALLTHROUGH;
199
    case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC:
200
        assert(PyErr_Occurred());
201
        break;
202
    case _Py_ext_module_loader_result_ERR_MISSING: _Py_FALLTHROUGH;
203
    case _Py_ext_module_loader_result_ERR_UNINITIALIZED: _Py_FALLTHROUGH;
204
    case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: _Py_FALLTHROUGH;
205
    case _Py_ext_module_loader_result_ERR_NOT_MODULE: _Py_FALLTHROUGH;
206
    case _Py_ext_module_loader_result_ERR_MISSING_DEF:
207
        assert(!PyErr_Occurred());
208
        break;
209
    default:
210
        /* We added a new error kind but forgot to add it to this switch. */
211
        assert(0);
212
    }
213
#endif
214
215
0
    assert(res->err == NULL && res->_err.exc == NULL);
216
0
    res->err = &res->_err;
217
0
    *res->err = (struct _Py_ext_module_loader_result_error){
218
0
        .kind=kind,
219
0
        .exc=PyErr_GetRaisedException(),
220
0
    };
221
222
    /* For some kinds, we also set/check res->kind. */
223
0
    switch (kind) {
224
0
    case _Py_ext_module_loader_result_ERR_UNINITIALIZED:
225
0
        assert(res->kind == _Py_ext_module_kind_UNKNOWN);
226
0
        res->kind = _Py_ext_module_kind_INVALID;
227
0
        break;
228
    /* None of the rest affect the result kind. */
229
0
    case _Py_ext_module_loader_result_EXCEPTION: _Py_FALLTHROUGH;
230
0
    case _Py_ext_module_loader_result_ERR_MISSING: _Py_FALLTHROUGH;
231
0
    case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC: _Py_FALLTHROUGH;
232
0
    case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: _Py_FALLTHROUGH;
233
0
    case _Py_ext_module_loader_result_ERR_NOT_MODULE: _Py_FALLTHROUGH;
234
0
    case _Py_ext_module_loader_result_ERR_MISSING_DEF:
235
0
        break;
236
0
    default:
237
        /* We added a new error kind but forgot to add it to this switch. */
238
0
        assert(0);
239
0
    }
240
0
}
241
242
void
243
_Py_ext_module_loader_result_apply_error(
244
                            struct _Py_ext_module_loader_result *res,
245
                            const char *name)
246
0
{
247
0
    assert(!PyErr_Occurred());
248
0
    assert(res->err != NULL && res->err == &res->_err);
249
0
    struct _Py_ext_module_loader_result_error err = *res->err;
250
0
    res->err = NULL;
251
252
    /* We're otherwise done with the result at this point. */
253
0
    _Py_ext_module_loader_result_clear(res);
254
255
#ifndef NDEBUG
256
    switch (err.kind) {
257
    case _Py_ext_module_loader_result_EXCEPTION: _Py_FALLTHROUGH;
258
    case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC:
259
        assert(err.exc != NULL);
260
        break;
261
    case _Py_ext_module_loader_result_ERR_MISSING: _Py_FALLTHROUGH;
262
    case _Py_ext_module_loader_result_ERR_UNINITIALIZED: _Py_FALLTHROUGH;
263
    case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE: _Py_FALLTHROUGH;
264
    case _Py_ext_module_loader_result_ERR_NOT_MODULE: _Py_FALLTHROUGH;
265
    case _Py_ext_module_loader_result_ERR_MISSING_DEF:
266
        assert(err.exc == NULL);
267
        break;
268
    default:
269
        /* We added a new error kind but forgot to add it to this switch. */
270
        assert(0);
271
    }
272
#endif
273
274
0
    const char *msg = NULL;
275
0
    switch (err.kind) {
276
0
    case _Py_ext_module_loader_result_EXCEPTION:
277
0
        break;
278
0
    case _Py_ext_module_loader_result_ERR_MISSING:
279
0
        msg = "initialization of %s failed without raising an exception";
280
0
        break;
281
0
    case _Py_ext_module_loader_result_ERR_UNREPORTED_EXC:
282
0
        msg = "initialization of %s raised unreported exception";
283
0
        break;
284
0
    case _Py_ext_module_loader_result_ERR_UNINITIALIZED:
285
0
        msg = "init function of %s returned uninitialized object";
286
0
        break;
287
0
    case _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE:
288
0
        msg = "initialization of %s did not return PyModuleDef";
289
0
        break;
290
0
    case _Py_ext_module_loader_result_ERR_NOT_MODULE:
291
0
        msg = "initialization of %s did not return an extension module";
292
0
        break;
293
0
    case _Py_ext_module_loader_result_ERR_MISSING_DEF:
294
0
        msg = "initialization of %s did not return a valid extension module";
295
0
        break;
296
0
    default:
297
        /* We added a new error kind but forgot to add it to this switch. */
298
0
        assert(0);
299
0
        PyErr_Format(PyExc_SystemError,
300
0
                     "loading %s failed due to init function", name);
301
0
        return;
302
0
    }
303
304
0
    if (err.exc != NULL) {
305
0
        PyErr_SetRaisedException(err.exc);
306
0
        err.exc = NULL;  /* PyErr_SetRaisedException() stole our reference. */
307
0
        if (msg != NULL) {
308
0
            _PyErr_FormatFromCause(PyExc_SystemError, msg, name);
309
0
        }
310
0
    }
311
0
    else {
312
0
        assert(msg != NULL);
313
0
        PyErr_Format(PyExc_SystemError, msg, name);
314
0
    }
315
0
}
316
317
318
/********************************************/
319
/* getting/running the module init function */
320
/********************************************/
321
322
#ifdef HAVE_DYNAMIC_LOADING
323
static dl_funcptr
324
findfuncptr(const char *prefix, const char *name_buf,
325
            struct _Py_ext_module_loader_info *info,
326
            FILE *fp)
327
402
{
328
#ifdef MS_WINDOWS
329
    return _PyImport_FindSharedFuncptrWindows(
330
            prefix, name_buf, info->filename, fp);
331
#else
332
402
    const char *path_buf = PyBytes_AS_STRING(info->filename_encoded);
333
402
    return _PyImport_FindSharedFuncptr(
334
402
            prefix, name_buf, path_buf, fp);
335
402
#endif
336
402
}
337
338
int
339
_PyImport_GetModuleExportHooks(
340
    struct _Py_ext_module_loader_info *info,
341
    FILE *fp,
342
    PyModInitFunction *modinit,
343
    PyModExportFunction *modexport)
344
203
{
345
203
    *modinit = NULL;
346
203
    *modexport = NULL;
347
348
203
    const char *name_buf = PyBytes_AS_STRING(info->name_encoded);
349
203
    dl_funcptr exportfunc;
350
351
203
    exportfunc = findfuncptr(
352
203
        info->hook_prefixes->export_prefix,
353
203
        name_buf, info, fp);
354
203
    if (exportfunc) {
355
4
        *modexport = (PyModExportFunction)exportfunc;
356
4
        return 2;
357
4
    }
358
199
    if (PyErr_Occurred()) {
359
0
        return -1;
360
0
    }
361
362
199
    exportfunc = findfuncptr(
363
199
        info->hook_prefixes->init_prefix,
364
199
        name_buf, info, fp);
365
199
    if (exportfunc) {
366
199
        *modinit = (PyModInitFunction)exportfunc;
367
199
        return 1;
368
199
    }
369
370
0
    if (!PyErr_Occurred()) {
371
0
        PyObject *msg;
372
0
        msg = PyUnicode_FromFormat(
373
0
            "dynamic module does not define "
374
0
            "module export function (%s_%s or %s_%s)",
375
0
            info->hook_prefixes->export_prefix, name_buf,
376
0
            info->hook_prefixes->init_prefix, name_buf);
377
0
        if (msg != NULL) {
378
0
            PyErr_SetImportError(msg, info->name, info->filename);
379
0
            Py_DECREF(msg);
380
0
        }
381
0
    }
382
0
    return -1;
383
199
}
384
#endif /* HAVE_DYNAMIC_LOADING */
385
386
int
387
_PyImport_RunModInitFunc(PyModInitFunction p0,
388
                         struct _Py_ext_module_loader_info *info,
389
                         struct _Py_ext_module_loader_result *p_res)
390
952
{
391
952
    struct _Py_ext_module_loader_result res = {
392
952
        .kind=_Py_ext_module_kind_UNKNOWN,
393
952
    };
394
395
    /* Call the module init function. */
396
397
    /* Package context is needed for single-phase init */
398
952
    const char *oldcontext = _PyImport_SwapPackageContext(info->newcontext);
399
952
    PyObject *m = p0();
400
952
    _PyImport_SwapPackageContext(oldcontext);
401
402
    /* Validate the result (and populate "res". */
403
404
952
    if (m == NULL) {
405
        /* The init func for multi-phase init modules is expected
406
         * to return a PyModuleDef after calling PyModuleDef_Init().
407
         * That function never raises an exception nor returns NULL,
408
         * so at this point it must be a single-phase init modules. */
409
0
        res.kind = _Py_ext_module_kind_SINGLEPHASE;
410
0
        if (PyErr_Occurred()) {
411
0
            _Py_ext_module_loader_result_set_error(
412
0
                        &res, _Py_ext_module_loader_result_EXCEPTION);
413
0
        }
414
0
        else {
415
0
            _Py_ext_module_loader_result_set_error(
416
0
                        &res, _Py_ext_module_loader_result_ERR_MISSING);
417
0
        }
418
0
        goto error;
419
952
    } else if (PyErr_Occurred()) {
420
        /* Likewise, we infer that this is a single-phase init module. */
421
0
        res.kind = _Py_ext_module_kind_SINGLEPHASE;
422
0
        _Py_ext_module_loader_result_set_error(
423
0
                &res, _Py_ext_module_loader_result_ERR_UNREPORTED_EXC);
424
        /* We would probably be correct to decref m here,
425
         * but we weren't doing so before,
426
         * so we stick with doing nothing. */
427
0
        m = NULL;
428
0
        goto error;
429
0
    }
430
431
952
    if (Py_IS_TYPE(m, NULL)) {
432
        /* This can happen when a PyModuleDef is returned without calling
433
         * PyModuleDef_Init on it
434
         */
435
0
        _Py_ext_module_loader_result_set_error(
436
0
                &res, _Py_ext_module_loader_result_ERR_UNINITIALIZED);
437
        /* Likewise, decref'ing here makes sense.  However, the original
438
         * code has a note about "prevent segfault in DECREF",
439
         * so we play it safe and leave it alone. */
440
0
        m = NULL; /* prevent segfault in DECREF */
441
0
        goto error;
442
0
    }
443
444
952
    if (PyObject_TypeCheck(m, &PyModuleDef_Type)) {
445
        /* multi-phase init */
446
952
        res.kind = _Py_ext_module_kind_MULTIPHASE;
447
952
        res.def = (PyModuleDef *)m;
448
        /* Run PyModule_FromDefAndSpec() to finish loading the module. */
449
952
    }
450
0
    else if (info->hook_prefixes == &nonascii_prefixes) {
451
        /* Non-ASCII is only supported for multi-phase init. */
452
0
        res.kind = _Py_ext_module_kind_MULTIPHASE;
453
        /* Don't allow legacy init for non-ASCII module names. */
454
0
        _Py_ext_module_loader_result_set_error(
455
0
                &res, _Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE);
456
0
        goto error;
457
0
    }
458
0
    else {
459
        /* single-phase init (legacy) */
460
0
        res.kind = _Py_ext_module_kind_SINGLEPHASE;
461
0
        res.module = m;
462
463
0
        if (!PyModule_Check(m)) {
464
0
            _Py_ext_module_loader_result_set_error(
465
0
                    &res, _Py_ext_module_loader_result_ERR_NOT_MODULE);
466
0
            goto error;
467
0
        }
468
469
0
        res.def = _PyModule_GetDefOrNull(m);
470
0
        if (res.def == NULL) {
471
0
            PyErr_Clear();
472
0
            _Py_ext_module_loader_result_set_error(
473
0
                    &res, _Py_ext_module_loader_result_ERR_MISSING_DEF);
474
0
            goto error;
475
0
        }
476
0
    }
477
478
952
    assert(!PyErr_Occurred());
479
952
    assert(res.err == NULL);
480
952
    *p_res = res;
481
952
    return 0;
482
483
0
error:
484
0
    assert(!PyErr_Occurred());
485
0
    assert(res.err != NULL);
486
0
    Py_CLEAR(res.module);
487
    res.def = NULL;
488
0
    *p_res = res;
489
0
    p_res->err = &p_res->_err;
490
0
    return -1;
491
952
}