You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行PAM函数触发Segmentation Fault问题排查求助

排查PAM模块嵌入Python触发Segmentation Fault的问题

刚接触C语言踩这种坑太正常了,我帮你拆解下代码里几个绝对会触发段错误的点,还有一些隐藏的风险:

1. 最致命的错误:直接把PyObject*和整数比较

你这段代码里的判断:

if (ret1 == 1){

完全错误!ret1是PyObject*类型(指针),而1是整数。你这里是在比较指针地址是否等于0x1,这几乎不可能成立,而且如果ret1是NULL(函数调用失败),或者返回的是Python的True/False对象,这种比较都会导致非法内存访问,直接触发段错误。

正确的做法是用Python的API来判断返回值:

  • 如果你的detect()函数返回Python的布尔值,用PyBool_Check(ret1)和ret1 == Py_True来判断
  • 如果返回整数,用PyLong_Check(ret1)然后转成C整数再判断

2. 缺少关键的空指针检查

你的代码里很多地方没有检查API调用的返回值,比如:

  • dlopen的返回值没检查,如果加载失败会返回NULL,后续操作会出问题
  • PyObject_GetAttrString获取函数后,没检查func1是否是可调用对象(比如模块里没有这个函数,或者它不是函数),直接调用PyObject_CallObject会触发段错误
  • PyObject_CallObject的返回值ret1可能是NULL(函数调用抛出异常),你直接用它也会出问题

3. 手动dlopen Python库是多余且危险的

Py_Initialize()会自动加载Python的共享库,你手动调用dlopen会导致重复加载,可能引发符号冲突或者内存错误,直接删掉这一行就行。

4. chdir的风险

你调用chdir("../code/facial")但没有检查是否成功,如果这个目录不存在,或者PAM模块运行的用户(比如root)没有权限访问,后续Python导入模块会失败,甚至可能导致其他奇怪的错误。一定要检查chdir的返回值:

if (chdir("../code/facial") != 0) {
    perror("Failed to change directory");
    return PAM_AUTH_ERR;
}

5. Python解释器的初始化和销毁时机问题

PAM模块可能会被多次调用(比如同一个进程里多次认证),Py_Initialize()和Py_Finalize()不应该每次认证都调用,频繁初始化销毁解释器不仅效率低,还可能导致内存泄漏或者段错误。建议在模块初始化时只初始化一次Python解释器,模块销毁时再销毁。

修正后的代码示例

我把你的代码改了一下,修复了上面的问题:

#define PY_SSIZE_T_CLEAN
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
//#define PAM_SM_SESSION
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include </usr/include/python3.6m/Python.h>

// 全局标记,确保Python解释器只初始化一次
static int python_initialized = 0;

/* expected hook */
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
    return PAM_SUCCESS;
}

PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    printf("Acct mgmt\n");
    return PAM_SUCCESS;
}

/* expected hook, this is where custom stuff happens */
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
    // 检查目录切换是否成功
    if (chdir("../code/facial") != 0) {
        perror("chdir failed");
        return PAM_AUTH_ERR;
    }

    // 只初始化一次Python解释器
    if (!python_initialized) {
        Py_Initialize();
        // 添加当前目录到Python路径
        PyRun_SimpleString("import os\nimport sys\nsys.path.append(os.getcwd())");
        python_initialized = 1;
    }

    PyObject *mymod, *func1, *ret1;
    mymod = PyImport_ImportModule("pam_detect");
    if (mymod == NULL) {
        PyErr_Print(); // 打印Python导入错误,方便排查
        return PAM_AUTH_ERR;
    }

    func1 = PyObject_GetAttrString(mymod, "detect");
    // 检查func1是否是可调用对象
    if (func1 == NULL || !PyCallable_Check(func1)) {
        if (func1 == NULL) PyErr_Print();
        else printf("detect is not a callable function\n");
        Py_XDECREF(mymod);
        return PAM_AUTH_ERR;
    }

    // 调用函数,无参数
    ret1 = PyObject_CallObject(func1, NULL);
    if (ret1 == NULL) {
        PyErr_Print(); // 打印函数调用时的Python异常
        Py_XDECREF(func1);
        Py_XDECREF(mymod);
        return PAM_AUTH_ERR;
    }

    int auth_result = PAM_AUTH_ERR;
    // 假设detect返回布尔值
    if (PyBool_Check(ret1)) {
        if (ret1 == Py_True) {
            auth_result = PAM_SUCCESS;
        }
    } 
    // 如果返回整数,用下面的判断
    else if (PyLong_Check(ret1)) {
        long val = PyLong_AsLong(ret1);
        if (val == 1) {
            auth_result = PAM_SUCCESS;
        }
    }
    else {
        printf("detect returned unexpected type\n");
    }

    // 释放Python对象的引用,避免内存泄漏
    Py_XDECREF(ret1);
    Py_XDECREF(func1);
    Py_XDECREF(mymod);

    // 注意:不要在这里调用Py_Finalize(),留到模块销毁时处理
    return auth_result;
}

// 模块卸载时销毁Python解释器
PAM_EXTERN void _pam_destroy(pam_handle_t *pamh) {
    if (python_initialized) {
        Py_Finalize();
        python_initialized = 0;
    }
}

额外的排查技巧

  • 用gdb调试PAM模块:比如运行gdb --args sshd -d,然后触发认证,当段错误发生时,用bt命令查看调用栈,就能定位到具体哪一行出错
  • 打开Python的错误输出:在Py_Initialize()后调用PyErr_Print(),能看到Python层面的错误(比如模块导入失败、函数调用抛出异常),这些错误经常会间接导致C层面的段错误

内容的提问来源于stack exchange,提问作者Pythoncrypt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 16:32:42