Coverage Report

Created: 2025-11-30 06:38

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
124
get_encoded_name(PyObject *name, const struct hook_prefixes **hook_prefixes) {
30
124
    PyObject *tmp;
31
124
    PyObject *encoded = NULL;
32
124
    PyObject *modname = NULL;
33
124
    Py_ssize_t name_len, lastdot;
34
35
    /* Get the short name (substring after last dot) */
36
124
    name_len = PyUnicode_GetLength(name);
37
124
    if (name_len < 0) {
38
0
        return NULL;
39
0
    }
40
124
    lastdot = PyUnicode_FindChar(name, '.', 0, name_len, -1);
41
124
    if (lastdot < -1) {
42
0
        return NULL;
43
124
    } 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
124
    } else {
50
124
        Py_INCREF(name);
51
124
    }
52
53
    /* Encode to ASCII or Punycode, as needed */
54
124
    encoded = PyUnicode_AsEncodedString(name, "ascii", NULL);
55
124
    if (encoded != NULL) {
56
124
        *hook_prefixes = &ascii_only_prefixes;
57
124
    } 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
124
    modname = _PyObject_CallMethod(encoded, &_Py_ID(replace), "cc", '-', '_');
72
124
    if (modname == NULL)
73
0
        goto error;
74
75
124
    Py_DECREF(name);
76
124
    Py_DECREF(encoded);
77
124
    return modname;
78
0
error:
79
0
    Py_DECREF(name);
80
0
    Py_XDECREF(encoded);
81
0
    return NULL;
82
124
}
83
84
void
85
_Py_ext_module_loader_info_clear(struct _Py_ext_module_loader_info *info)
86
684
{
87
684
    Py_CLEAR(info->filename);
88
684
#ifndef MS_WINDOWS
89
684
    Py_CLEAR(info->filename_encoded);
90
684
#endif
91
684
    Py_CLEAR(info->name);
92
684
    Py_CLEAR(info->name_encoded);
93
684
}
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
124
{
100
124
    struct _Py_ext_module_loader_info info = {
101
124
        .origin=origin,
102
124
    };
103
104
124
    assert(name != NULL);
105
124
    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
124
    assert(PyUnicode_GetLength(name) > 0);
112
124
    info.name = Py_NewRef(name);
113
114
124
    info.name_encoded = get_encoded_name(info.name, &info.hook_prefixes);
115
124
    if (info.name_encoded == NULL) {
116
0
        _Py_ext_module_loader_info_clear(&info);
117
0
        return -1;
118
0
    }
119
120
124
    info.newcontext = PyUnicode_AsUTF8(info.name);
121
124
    if (info.newcontext == NULL) {
122
0
        _Py_ext_module_loader_info_clear(&info);
123
0
        return -1;
124
0
    }
125
126
124
    if (filename != NULL) {
127
124
        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
124
        info.filename = Py_NewRef(filename);
134
135
124
#ifndef MS_WINDOWS
136
124
        info.filename_encoded = PyUnicode_EncodeFSDefault(info.filename);
137
124
        if (info.filename_encoded == NULL) {
138
0
            _Py_ext_module_loader_info_clear(&info);
139
0
            return -1;
140
0
        }
141
124
#endif
142
143
124
        info.path = info.filename;
144
124
    }
145
0
    else {
146
0
        info.path = info.name;
147
0
    }
148
149
124
    *p_info = info;
150
124
    return 0;
151
124
}
152
153
int
154
_Py_ext_module_loader_info_init_for_builtin(
155
                            struct _Py_ext_module_loader_info *info,
156
                            PyObject *name)
157
560
{
158
560
    assert(PyUnicode_Check(name));
159
560
    assert(PyUnicode_GetLength(name) > 0);
160
161
560
    PyObject *name_encoded = PyUnicode_AsEncodedString(name, "ascii", NULL);
162
560
    if (name_encoded == NULL) {
163
0
        return -1;
164
0
    }
165
166
560
    *info = (struct _Py_ext_module_loader_info){
167
560
        .name=Py_NewRef(name),
168
560
        .name_encoded=name_encoded,
169
        /* We won't need filename. */
170
560
        .path=name,
171
560
        .origin=_Py_ext_module_origin_BUILTIN,
172
560
        .hook_prefixes=&ascii_only_prefixes,
173
560
        .newcontext=NULL,
174
560
    };
175
560
    return 0;
176
560
}
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
124
{
196
124
    PyObject *name = PyObject_GetAttrString(spec, "name");
197
124
    if (name == NULL) {
198
0
        return -1;
199
0
    }
200
124
    PyObject *filename = PyObject_GetAttrString(spec, "origin");
201
124
    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
124
    _Py_ext_module_origin origin = _Py_ext_module_origin_DYNAMIC;
207
124
    int err = _Py_ext_module_loader_info_init(p_info, name, filename, origin);
208
124
    Py_DECREF(name);
209
124
    Py_DECREF(filename);
210
124
    return err;
211
124
}
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
684
{
222
    /* Instead, the caller should have called
223
     * _Py_ext_module_loader_result_apply_error(). */
224
684
    assert(res->err == NULL);
225
684
    *res = (struct _Py_ext_module_loader_result){0};
226
684
}
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
248
{
365
#ifdef MS_WINDOWS
366
    return _PyImport_FindSharedFuncptrWindows(
367
            prefix, name_buf, info->filename, fp);
368
#else
369
248
    const char *path_buf = PyBytes_AS_STRING(info->filename_encoded);
370
248
    return _PyImport_FindSharedFuncptr(
371
248
            prefix, name_buf, path_buf, fp);
372
248
#endif
373
248
}
374
375
int
376
_PyImport_GetModuleExportHooks(
377
    struct _Py_ext_module_loader_info *info,
378
    FILE *fp,
379
    PyModInitFunction *modinit,
380
    PyModExportFunction *modexport)
381
124
{
382
124
    *modinit = NULL;
383
124
    *modexport = NULL;
384
385
124
    const char *name_buf = PyBytes_AS_STRING(info->name_encoded);
386
124
    dl_funcptr exportfunc;
387
388
124
    exportfunc = findfuncptr(
389
124
        info->hook_prefixes->export_prefix,
390
124
        name_buf, info, fp);
391
124
    if (exportfunc) {
392
0
        *modexport = (PyModExportFunction)exportfunc;
393
0
        return 2;
394
0
    }
395
124
    if (PyErr_Occurred()) {
396
0
        return -1;
397
0
    }
398
399
124
    exportfunc = findfuncptr(
400
124
        info->hook_prefixes->init_prefix,
401
124
        name_buf, info, fp);
402
124
    if (exportfunc) {
403
124
        *modinit = (PyModInitFunction)exportfunc;
404
124
        return 1;
405
124
    }
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
124
}
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
684
{
428
684
    struct _Py_ext_module_loader_result res = {
429
684
        .kind=_Py_ext_module_kind_UNKNOWN,
430
684
    };
431
432
    /* Call the module init function. */
433
434
    /* Package context is needed for single-phase init */
435
684
    const char *oldcontext = _PyImport_SwapPackageContext(info->newcontext);
436
684
    PyObject *m = p0();
437
684
    _PyImport_SwapPackageContext(oldcontext);
438
439
    /* Validate the result (and populate "res". */
440
441
684
    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
684
    } 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
684
    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
684
    if (PyObject_TypeCheck(m, &PyModuleDef_Type)) {
482
        /* multi-phase init */
483
684
        res.kind = _Py_ext_module_kind_MULTIPHASE;
484
684
        res.def = (PyModuleDef *)m;
485
        /* Run PyModule_FromDefAndSpec() to finish loading the module. */
486
684
    }
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
684
    assert(!PyErr_Occurred());
516
684
    assert(res.err == NULL);
517
684
    *p_res = res;
518
684
    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
684
}