关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » SQL语言 »编写安全的扩展存储过程

    编写安全的扩展存储过程

    2015-11-29 00:00:00 出处:ITJS
    分享

    近日在写一个扩展存储过程时,发现再写这类动态库时,还是有一些需要特别注意的地方。之所以会特别注意,是因为DLL运行于SQL Server的地址空间,而SQL Server到底是怎么进行线程调度的,却不是我们能了解的,即便了解也无法控制。
    我们写动态库一般是自己用,即便给别人用,也很少像SQL Server这样,一个动态库很有可能加载多次,并且都是加载到一个进程的地址空间中。我们知道,当一个动态库加载到进程的地址空间时,DLL所有全局与局部变量初始化且仅初始化一次,以后再次调用 LoadLibrary函数时,仅仅增加其引用计数而已,那么很显然,假如有一全局 int,初始化为0,调用一个函数另其自加,此时其值为1,然后再调用LoadLibray,并利用返回的句柄调用输出函数输出该值,虽然调用者觉得自己加载后立即输出,然后该值确实1而不是0。windows是进程独立的,而在线程方面,假如不注意,上面的情况很可能会程序员带来麻烦。
    介绍一下我的扩展存储过程,该动态库导出了三个函数:Init,work,Final,Init读文件,存储信息于内存,work简单的只是向该内存检索信息,Final回收内存。如上所说,假如不考虑同一进程空间多次加载问题,两次调用Init将造成无谓的浪费,因为我第一次已经读进了内存,要是通过堆分配内存,还会造成内存泄露。
    我使用的引用计数解决的该问题,代码很短,直接贴上来:

    #include "stdafx.h"
    #include <string>
    using namespace std;
    extern "C" {
    RETCODE __declspec(dllexport) xp_part_init(SRV_PROC *srvproc);
    RETCODE __declspec(dllexport) xp_part_process(SRV_PROC *srvproc);
    RETCODE __declspec(dllexport) xp_part_finalize(SRV_PROC *srvproc);
    }
    #define XP_NOERROR 0
    #define XP_ERROR 1
    HINSTANCE hInst = NULL;
    int nRef = 0;
    void printError (SRV_PROC *pSrvProc, CHAR* szErrorMsg);
    ULONG __GetXpVersion(){ return ODS_VERSION;}
    SRVRETCODE xp_part_init(SRV_PROC* pSrvProc){
    typedef bool (*Func)();
    if(nRef == 0){
    hInst = ::LoadLibrary("part.dll");
    if(hInst == NULL){
    printError(pSrvProc,"不能加载part.dll");
    return XP_ERROR;
    }
    Func theFunc = (Func)::GetProcAddress(hInst,"Init");
    if(!theFunc()){
    ::FreeLibrary(hInst);
    printError(pSrvProc,"不能获得分类号与专辑的对应表");
    return XP_ERROR;
    }
    }
    ++ nRef;
    return (XP_NOERROR);
    }
    SRVRETCODE xp_part_process(SRV_PROC* pSrvProc){
    typedef bool (*Func)(char*);
    if(nRef == 0){
    printError(pSrvProc,"函数尚未初始化,请首先调用xp_part_init");
    return XP_ERROR;
    }
    Func theFunc = (Func)::GetProcAddress(hInst,"Get");
    BYTE bType;
    ULONG cbMaxLen,cbActualLen;
    BOOL fNull;
    char szInput[256] = {0};
    if (srv_paraminfo(pSrvProc, 1, &bType, (ULONG*)&cbMaxLen, (ULONG*)&cbActualLen,

    (BYTE*)szInput, &fNull) == FAIL){
    printError(pSrvProc,"srv_paraminfo 返回 FAIL");
    return XP_ERROR;
    }
    szInput[cbActualLen] = 0;
    string strInput = szInput;
    string strOutput = ";";
    int cur,old = 0;
    while(string::npos != (cur = strInput.find(’;’,old)) ){
    strncpy(szInput,strInput.c_str() + old,cur - old);
    szInput[cur - old] = 0;
    old = cur + 1;
    theFunc(szInput);
    if(string::npos ==strOutput.find((string)";" + szInput))
    strOutput += szInput;
    }
    strcpy(szInput,strOutput.c_str());
    if (FAIL == srv_paramsetoutput(pSrvProc, 1, (BYTE*)(szInput + 1),

    strlen(szInput) - 1,FALSE)){
    printError (pSrvProc, "srv_paramsetoutput 调用失败");
    return XP_ERROR;
    }
    srv_senddone(pSrvProc, (SRV_DONE_COUNT | SRV_DONE_MORE), 0, 0);
    return XP_NOERROR;
    }
    SRVRETCODE xp_part_finalize(SRV_PROC* pSrvProc){
    typedef void (*Func)();
    if(nRef == 0)
    return XP_NOERROR;
    Func theFunc = (Func)::GetProcAddress(hInst,"Fin");
    if((--nRef) == 0){
    theFunc();
    ::FreeLibrary(hInst);
    hInst = NULL;
    }
    return (XP_NOERROR);
    }

    我想虽然看上去不是很高明,然而问题应该是解决了的。
    还有一点说明,为什么不使用Tls,老实说,我考虑过使用的,因为其实代码是有一点问题的,假如一个用户调用xp_part_init,然后另一个用户也调用xp_part_init,注意我们的存储过程可是服务器端的,然后第一个用户调用xp_part_finalize,那么会怎样,他仍然可以正常使用xp_part_process,这倒无所谓,然而第一个用户调用两次xp_part_finalize,就能够影响第二个用户了,他的xp_part_process将返回错误。
    使用Tls 似乎可以解决这问题,例如再添加一个tls_index变量,调用 TlsSetValue保存用户私人数据,TlsGetValue检索私人数据,当xp_part_init时,假如该私人数据为0,执行正常的初始化过程,(即上面的xp_part_init)执行成功后存储私人数据为1,假如是1,直接返回,xp_part_finalize时,假如私人数据为1,则执行正常的xp_part_finalize,然后设私人数据为0,假如是0,直接返回。
    好像想法还是不错的,这样隔离了多个用户,安全性似乎提高了不少,然而事实是不可行的。因为Tls保存的并不是私人数据,而是线程本地变量,我们不能保证一个用户的多次操作都是用同一个线程执行的,这个由SQL Server自己控制,事实上我在查询分析器里多次执行的结果显示,SQL Server内部似乎使用了一个线程池。既然如此,那这种想法也只能作罢。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。