Coverage Report

Created: 2025-11-11 06:44

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