Write a secure SQL Server extended stored procedure

Source: Internet
Author: User
Tags nref
SQL Server's extended storage process is actually a common WindowsDLL, but some functions are implemented according to certain rules. Recently, when writing an extended storage process, I found that there are still some important points to note when writing this kind of dynamic library. Note that the DLL runs in the address space of SQLServer.

SQL Server's extended storage process is actually a common Windows DLL, but some functions are implemented according to certain rules. Recently, when writing an extended storage process, I found that there are still some important points to note when writing this kind of dynamic library. Note that the DLL runs in the address space of SQL Server.

SQL Server Extension Storage ProcessIs a common Windows DLL, but some functions are implemented according to certain rules.

I am writing Extension Storage ProcessWhen you find that writing such dynamic libraries again, there are still some areas that need special attention. The reason for special attention is that DLL runs in the address space of SQL Server, and SQL Server does not know how to schedule threads, even if we know it, it cannot be controlled.

We write a dynamic database for our own use. Even if it is used by others, it is rarely like SQL Server. A dynamic library is likely to be loaded multiple times, and are all loaded into the address space of a process. We know that when a dynamic library is loaded to the address space of the process, all global and local variables of the DLL are initialized and initialized only once. When the LoadLibrary function is called again, it only adds the reference count. Obviously, if there is a global int, the initialization is 0, and a function is called to add itself, the value is 1, and then LoadLibray is called, and use the returned handle to call the output function to output the value. Although the caller feels that the value is output immediately after loading, the value is indeed 1 rather than 0. Windows is a process independent, and in terms of threads, if you do not pay attention to it, the above situation is likely to cause trouble for programmers.

About my Extension Storage ProcessThe dynamic library exports three functions: Init, work, Final, Init read file, StorageThe information is stored in the memory. The work simply retrieves information from the memory and the Final recycles the memory. As mentioned above, if the problem of loading the same process space multiple times is not considered, calling Init twice will cause unnecessary waste because I have read the memory for the first time. If I allocate the memory through the heap, it also causes memory leakage.

The reference count I used solves this problem. The code is very short and can be directly pasted as follows:

# Include "stdafx. h"
# Include

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 cannot be loaded ");
Return XP_ERROR;
}
Func theFunc = (Func): GetProcAddress (hInst, "Init ");
If (! TheFunc ()){
: FreeLibrary (hInst );
PrintError (pSrvProc, "the table corresponding to the album category cannot be obtained ");
Return XP_ERROR;
}
}

++ NRef;
Return (XP_NOERROR );
}

SRVRETCODE xp_part_process (SRV_PROC * pSrvProc ){
Typedef bool (* Func) (char *);

If (nRef = 0 ){
PrintError (pSrvProc, "the function has not been initialized. Please call xp_part_init" First ");
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 returned 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 call failed ");
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 );
}


I think although it seems not very clever, the problem should be solved.

Another note is why Tls is not used. To be honest, I have considered using Tls because the code is actually problematic. If one user calls xp_part_init and the other calls xp_part_init, pay attention to our Storage ProcessHowever, on the server side, if the first user calls xp_part_finalize, then what will happen? The user can still use xp_part_process normally. This does not matter. However, the first user calls xp_part_finalize twice, the second user will be affected, and the xp_part_process will return an error.

Tls seems to solve this problem. For example, if you add another tls_index variable and call TlsSetValue to save your private data, TlsGetValue is used to retrieve private data. When xp_part_init is used, if the private data is 0, perform normal Initialization Process, (Xp_part_init above) after successful execution StorageIf the private data is 1 and 1 is returned directly, when xp_part_finalize, if the private data is 1, the normal xp_part_finalize is executed, and then the private data is set to 0. If the private data is 0, return directly.

It seems that the idea is good, so that multiple users are isolated, SecurityIt seems that the performance has improved a lot, but the fact is not feasible. Because Tls does not store private data but local variables in the thread, we cannot guarantee that a user's operations are performed in the same thread for multiple times, which is controlled by the SQL Server itself, in fact, the results of multiple executions in the query analyzer show that the SQL Server seems to use a thread pool internally. In this case, we can leave this idea alone.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.