Coverage Report

Created: 2026-02-26 06:53

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