Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions test/expected/surrogate-pair.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
connected
preparing SQL text ending with a dangling high surrogate
SQLPrepareW returned without reading past the supplied length
disconnecting
ok
118 changes: 118 additions & 0 deletions test/src/surrogate-pair-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Test UTF-16 surrogate-pair handling in wide-character ODBC APIs.
*
* SQLPrepareW() converts the SQLWCHAR input with ucs2_to_utf8(). A dangling
* high surrogate at the end of the caller-specified length must not make the
* driver read the following SQLWCHAR.
*/
#include <stdio.h>
#include <stdlib.h>

#include "common.h"

#ifdef WIN32
static SQLWCHAR *
alloc_guarded_high_surrogate(void **mapping)
{
SYSTEM_INFO info;
DWORD page_size;
DWORD old_protect;
char *mem;

GetSystemInfo(&info);
page_size = info.dwPageSize;
mem = VirtualAlloc(NULL, page_size * 2, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!mem)
return NULL;
if (!VirtualProtect(mem + page_size, page_size, PAGE_NOACCESS,
&old_protect))
{
VirtualFree(mem, 0, MEM_RELEASE);
return NULL;
}
*mapping = mem;
return (SQLWCHAR *) (mem + page_size - sizeof(SQLWCHAR));
}

static void
free_guarded_high_surrogate(void *mapping)
{
VirtualFree(mapping, 0, MEM_RELEASE);
}
#else
#include <sys/mman.h>
#include <unistd.h>

static SQLWCHAR *
alloc_guarded_high_surrogate(void **mapping)
{
long page_size;
char *mem;

page_size = sysconf(_SC_PAGESIZE);
if (page_size <= 0)
return NULL;

mem = mmap(NULL, (size_t) page_size * 2, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (MAP_FAILED == mem)
return NULL;
if (mprotect(mem + page_size, (size_t) page_size, PROT_NONE) != 0)
{
munmap(mem, (size_t) page_size * 2);
return NULL;
}
*mapping = mem;
return (SQLWCHAR *) (mem + page_size - sizeof(SQLWCHAR));
}

static void
free_guarded_high_surrogate(void *mapping)
{
long page_size = sysconf(_SC_PAGESIZE);

if (page_size > 0)
munmap(mapping, (size_t) page_size * 2);
}
#endif

int
main(void)
{
SQLRETURN rc;
HSTMT hstmt = SQL_NULL_HSTMT;
SQLWCHAR *sql;
void *mapping = NULL;

test_connect_ext("");

rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);
if (!SQL_SUCCEEDED(rc))
{
print_diag("failed to allocate statement handle", SQL_HANDLE_DBC, conn);
test_disconnect();
return 1;
}

sql = alloc_guarded_high_surrogate(&mapping);
if (!sql)
{
fprintf(stderr, "failed to allocate guarded SQLWCHAR buffer\n");
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
test_disconnect();
return 1;
}

sql[0] = 0xd800;
printf("preparing SQL text ending with a dangling high surrogate\n");
rc = SQLPrepareW(hstmt, sql, 1);
printf("SQLPrepareW returned without reading past the supplied length\n");

free_guarded_high_surrogate(mapping);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
test_disconnect();
printf("ok\n");

return 0;
}
3 changes: 2 additions & 1 deletion test/tests
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@ TESTBINS = exe/connect-test \
exe/interval-overflow-test \
exe/conn-settings-test \
exe/percent-decode-test \
exe/dbms-version-test
exe/dbms-version-test \
exe/surrogate-pair-test
4 changes: 3 additions & 1 deletion win_unicode.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ MYPRINTF(0, " newlen=" FORMAT_LEN, ilen);
len += sizeof(byte2code);
}
/* surrogate pair check for non ucs-2 code */
else if (surrog1_bits == (*wstr & surrog_check))
else if (surrog1_bits == (*wstr & surrog_check) &&
i + 1 < ilen &&
surrog2_bits == (wstr[1] & surrog_check))
{
surrd1 = (*wstr & ~surrog_check) + surrogate_adjust;
wstr++;
Expand Down