Skip to content

Commit 32a7856

Browse files
authored
Merge pull request #10297 from willus10245/fix-odbc-memory-allocation-error
cap max column sizes for mssql and oracle OTP-20328
2 parents 67583f6 + 471726d commit 32a7856

4 files changed

Lines changed: 256 additions & 85 deletions

File tree

lib/odbc/c_src/odbcserver.c

Lines changed: 125 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
database using the Microsoft ODBC API. The c-process is implemented
2929
using two threads the supervisor thread and the database handler thread.
3030
If the database thread should hang erlang can close the c-process down
31-
by sendig a shutdown request to the supervisor thread.
31+
by sending a shutdown request to the supervisor thread.
3232
3333
Erlang will start this c-process as a port-program and send information
3434
regarding inet-port numbers through the erlang-port.
@@ -62,7 +62,10 @@
6262
they are converted to string values.
6363
6464
[?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver, C_SrollableCursors,
65-
C_TupelRow, BinaryStrings, ConnectionStr]
65+
C_TupelRow, BinaryStrings, ExtendedErrors, 255, 1,
66+
MaxLongColumnSizeOctets (4 bytes, big-endian, 0 = default), ConnectionStr]
67+
Legacy layout: same first six bytes then ConnectionStr; optional tuning via
68+
ERL_ODBC_MAX_LONG_COLUMN_SIZE.
6669
[?CLOSE_CONNECTION]
6770
[?COMMIT_TRANSACTION, CommitMode]
6871
[?QUERY, SQLQuery]
@@ -80,6 +83,7 @@
8083
C_SrollableCursors - ?ON | ?OFF
8184
C_TupelRow - - ?ON | ?OFF
8285
BinaryStrings - ?ON | ?OFF
86+
ExtendedErrors - ?ON | ?OFF
8387
ConnectionStr - String
8488
CommitMode - ?COMMIT | ?ROLLBACK
8589
SQLQuery - String
@@ -105,6 +109,7 @@
105109
#include <stdlib.h>
106110
#include <string.h>
107111
#include <stdio.h>
112+
#include <errno.h>
108113

109114
#ifdef UNIX
110115
#include <unistd.h>
@@ -141,11 +146,12 @@ DWORD WINAPI database_handler(const char *port);
141146
#else
142147
void database_handler(const char *port);
143148
#endif
144-
static db_result_msg handle_db_request(byte *reqstring, db_state *state);
149+
static db_result_msg handle_db_request(byte *reqstring, size_t msg_len,
150+
db_state *state);
145151
static void supervise(const char *port);
146152
/* ----------------- ODBC functions --------------------------------------*/
147153

148-
static db_result_msg db_connect(byte *connStrIn, db_state *state);
154+
static db_result_msg db_connect(byte *args, size_t args_len, db_state *state);
149155
static db_result_msg db_close_connection(db_state *state);
150156
static db_result_msg db_end_tran(byte compleationtype, db_state *state);
151157
static db_result_msg db_query(byte *sql, db_state *state);
@@ -193,7 +199,7 @@ static byte * receive_erlang_port_msg(void);
193199
#ifdef WIN32
194200
static SOCKET connect_to_erlang(const char *port);
195201
static void send_msg(db_result_msg *msg, SOCKET socket);
196-
static byte *receive_msg(SOCKET socket);
202+
static byte *receive_msg(SOCKET socket, size_t *msg_len_out);
197203
static Boolean receive_msg_part(SOCKET socket,
198204
byte * buffer, size_t msg_len);
199205
static Boolean send_msg_part(SOCKET socket, byte * buffer, size_t msg_len);
@@ -202,7 +208,7 @@ static void init_winsock(void);
202208
#elif defined(UNIX)
203209
static int connect_to_erlang(const char *port);
204210
static void send_msg(db_result_msg *msg, int socket);
205-
static byte *receive_msg(int socket);
211+
static byte *receive_msg(int socket, size_t *msg_len_out);
206212
static Boolean receive_msg_part(int socket, byte * buffer, size_t msg_len);
207213
static Boolean send_msg_part(int socket, byte * buffer, size_t msg_len);
208214
static void close_socket(int socket);
@@ -333,7 +339,7 @@ void supervise(const char *port) {
333339
#endif
334340

335341
socket = connect_to_erlang(port);
336-
msg = receive_msg(socket);
342+
msg = receive_msg(socket, NULL);
337343

338344
if(msg[0] == SHUTDOWN) {
339345
reason = EXIT_SUCCESS;
@@ -357,7 +363,7 @@ DWORD WINAPI database_handler(const char *port)
357363
byte *request_buffer = NULL;
358364
db_state state =
359365
{NULL, NULL, NULL, NULL, 0, {NULL, 0, 0},
360-
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
366+
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, (SQLULEN)0};
361367
byte request_id;
362368
#ifdef WIN32
363369
SOCKET socket;
@@ -369,23 +375,26 @@ DWORD WINAPI database_handler(const char *port)
369375
socket = connect_to_erlang(port);
370376

371377
do {
372-
request_buffer = receive_msg(socket);
378+
{
379+
size_t req_len;
380+
request_buffer = receive_msg(socket, &req_len);
373381

374-
request_id = request_buffer[0];
375-
msg = handle_db_request(request_buffer, &state);
382+
request_id = request_buffer[0];
383+
msg = handle_db_request(request_buffer, req_len, &state);
384+
}
385+
386+
send_msg(&msg, socket); /* Send answer to erlang */
387+
388+
if (msg.dyn_alloc) {
389+
ei_x_free(&(state.dynamic_buffer));
390+
} else {
391+
free(msg.buffer);
392+
msg.buffer = NULL;
393+
}
394+
395+
free(request_buffer);
396+
request_buffer = NULL;
376397

377-
send_msg(&msg, socket); /* Send answer to erlang */
378-
379-
if (msg.dyn_alloc) {
380-
ei_x_free(&(state.dynamic_buffer));
381-
} else {
382-
free(msg.buffer);
383-
msg.buffer = NULL;
384-
}
385-
386-
free(request_buffer);
387-
request_buffer = NULL;
388-
389398
} while(request_id != CLOSE_CONNECTION);
390399

391400
shutdown(socket, 2);
@@ -400,47 +409,78 @@ DWORD WINAPI database_handler(const char *port)
400409
/* Description: Calls the appropriate function to handle the database
401410
request received from the erlang-process. Returns a message to send back
402411
to erlang. */
403-
static db_result_msg handle_db_request(byte *reqstring, db_state *state)
412+
static db_result_msg handle_db_request(byte *reqstring, size_t msg_len,
413+
db_state *state)
404414
{
405415
byte *args;
406416
byte request_id;
417+
size_t args_len;
407418

408419
/* First byte is an index that identifies the requested command the
409420
rest is the argument string. */
410421
request_id = reqstring[0];
411422
args = reqstring + sizeof(byte);
423+
args_len = (msg_len > sizeof(byte)) ? (msg_len - sizeof(byte)) : 0;
412424

413425
switch(request_id) {
414426
case OPEN_CONNECTION:
415-
return db_connect(args, state);
427+
return db_connect(args, args_len, state);
416428
case CLOSE_CONNECTION:
417-
return db_close_connection(state);
429+
return db_close_connection(state);
418430
case COMMIT_TRANSACTION:
419-
if(args[0] == COMMIT) {
420-
return db_end_tran((byte)SQL_COMMIT, state);
421-
} else { /* args[0] == ROLLBACK */
422-
return db_end_tran((byte)SQL_ROLLBACK, state);
423-
}
431+
if(args[0] == COMMIT) {
432+
return db_end_tran((byte)SQL_COMMIT, state);
433+
} else { /* args[0] == ROLLBACK */
434+
return db_end_tran((byte)SQL_ROLLBACK, state);
435+
}
424436
case QUERY:
425-
return db_query(args, state);
437+
return db_query(args, state);
426438
case SELECT_COUNT:
427-
return db_select_count(args, state);
439+
return db_select_count(args, state);
428440
case SELECT:
429-
return db_select(args, state);
441+
return db_select(args, state);
430442
case PARAM_QUERY:
431-
return db_param_query(args, state);
443+
return db_param_query(args, state);
432444
case DESCRIBE:
433-
return db_describe_table(args, state);
445+
return db_describe_table(args, state);
434446
default:
435-
DO_EXIT(EXIT_FAILURE); /* Should not happen */
436-
}
447+
DO_EXIT(EXIT_FAILURE); /* Should not happen */
448+
}
437449
}
438450

439451
/* ----------------- ODBC-functions ----------------------------------*/
452+
453+
static SQLULEN
454+
clamp_max_long_col_size(SQLULEN value)
455+
{
456+
if (value == (SQLULEN)0)
457+
return (SQLULEN)DEFAULT_MAX_LONG_COL_SIZE;
458+
if (value > (SQLULEN)HARD_MAX_LONG_COL_SIZE)
459+
return (SQLULEN)HARD_MAX_LONG_COL_SIZE;
460+
return value;
461+
}
462+
463+
static SQLULEN
464+
max_long_col_size_from_env(void)
465+
{
466+
const char *e = getenv("ERL_ODBC_MAX_LONG_COLUMN_SIZE");
467+
unsigned long v;
468+
char *end = NULL;
469+
470+
if (e == NULL || e[0] == '\0')
471+
return (SQLULEN)0;
472+
errno = 0;
473+
v = strtoul(e, &end, 10);
474+
if (end == e || *end != '\0')
475+
return (SQLULEN)0;
476+
if (errno == ERANGE)
477+
return (SQLULEN)0;
478+
return (SQLULEN)v;
479+
}
440480

441481
/* Description: Tries to open a connection to the database using
442482
<connStrIn>, returns a message indicating the outcome. */
443-
static db_result_msg db_connect(byte *args, db_state *state)
483+
static db_result_msg db_connect(byte *args, size_t args_len, db_state *state)
444484
{
445485
/*
446486
* Danil Onishchenko aka RubberCthulhu, alevandal@gmail.com. 2013.01.09.
@@ -465,14 +505,32 @@ static db_result_msg db_connect(byte *args, db_state *state)
465505
int erl_auto_commit_mode, erl_trace_driver,
466506
use_srollable_cursors, tuple_row_state, binary_strings,
467507
extended_errors;
508+
SQLULEN long_col_cfg;
468509

510+
if (args_len < 6)
511+
DO_EXIT(EXIT_FAILURE);
512+
469513
erl_auto_commit_mode = args[0];
470514
erl_trace_driver = args[1];
471515
use_srollable_cursors = args[2];
472516
tuple_row_state = args[3];
473517
binary_strings = args[4];
474518
extended_errors = args[5];
475-
connStrIn = args + 6 * sizeof(byte);
519+
520+
if (args_len >= 13 && args[6] == OPEN_CONNECTION_PROTO_V1_A &&
521+
args[7] == OPEN_CONNECTION_PROTO_V1_B) {
522+
long_col_cfg = ((SQLULEN)args[8] << 24) | ((SQLULEN)args[9] << 16) |
523+
((SQLULEN)args[10] << 8) | (SQLULEN)args[11];
524+
connStrIn = args + 12;
525+
max_long_column_size(state) = clamp_max_long_col_size(long_col_cfg);
526+
} else {
527+
connStrIn = args + 6;
528+
max_long_column_size(state) =
529+
clamp_max_long_col_size(max_long_col_size_from_env());
530+
}
531+
532+
if ((size_t)(connStrIn - args) >= args_len)
533+
DO_EXIT(EXIT_FAILURE);
476534

477535
if(tuple_row_state == ON) {
478536
tuple_row(state) = TRUE;
@@ -1325,9 +1383,9 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
13251383
SQLULEN size;
13261384

13271385
msg = encode_empty_message();
1328-
1386+
13291387
ei_x_encode_list_header(&dynamic_buffer(state), num_of_columns);
1330-
1388+
13311389
for (i = 0; i < num_of_columns; ++i) {
13321390

13331391
if(!sql_success(SQLDescribeCol(statement_handle(state),
@@ -1337,32 +1395,37 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
13371395
&nullable)))
13381396
DO_EXIT(EXIT_DESC);
13391397

1340-
if(size == 0 && (sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR))
1341-
size = MAXCOLSIZE;
1342-
1398+
if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR) {
1399+
if(size == 0) {
1400+
size = max_long_column_size(state);
1401+
} else if(size > max_long_column_size(state)) {
1402+
size = max_long_column_size(state);
1403+
}
1404+
}
1405+
13431406
(columns(state)[i]).type.decimal_digits = dec_digits;
13441407
(columns(state)[i]).type.sql = sql_type;
13451408
(columns(state)[i]).type.col_size = size;
1346-
1409+
13471410
msg = map_sql_2_c_column(&columns(state)[i], state);
13481411
if (msg.length > 0) {
13491412
return msg; /* An error has occurred */
13501413
} else {
13511414
if (columns(state)[i].type.len > 0) {
13521415
columns(state)[i].buffer =
13531416
(char *)safe_malloc(columns(state)[i].type.len);
1354-
1417+
13551418
if (columns(state)[i].type.c == SQL_C_BINARY) {
13561419
/* retrieved later by retrive_binary_data */
13571420
} else {
13581421
if(!sql_success(
1359-
SQLBindCol
1360-
(statement_handle(state),
1361-
(SQLSMALLINT)(i+1),
1362-
columns(state)[i].type.c,
1363-
columns(state)[i].buffer,
1364-
columns(state)[i].type.len,
1365-
&columns(state)[i].type.strlen_or_indptr)))
1422+
SQLBindCol
1423+
(statement_handle(state),
1424+
(SQLSMALLINT)(i+1),
1425+
columns(state)[i].type.c,
1426+
columns(state)[i].buffer,
1427+
columns(state)[i].type.len,
1428+
&columns(state)[i].type.strlen_or_indptr)))
13661429
DO_EXIT(EXIT_BIND);
13671430
}
13681431
ei_x_encode_string_len(&dynamic_buffer(state),
@@ -1395,9 +1458,9 @@ static db_result_msg encode_value_list(SQLSMALLINT num_of_columns,
13951458
result = SQLFetch(statement_handle(state));
13961459

13971460
if (result == SQL_NO_DATA) /* Reached end of result set */
1398-
{
1399-
break;
1400-
}
1461+
{
1462+
break;
1463+
}
14011464

14021465
ei_x_encode_list_header(&dynamic_buffer(state), 1);
14031466

@@ -1956,9 +2019,9 @@ static void close_socket(int socket)
19562019
#endif
19572020

19582021
#ifdef WIN32
1959-
static byte * receive_msg(SOCKET socket)
2022+
static byte * receive_msg(SOCKET socket, size_t *msg_len_out)
19602023
#elif defined(UNIX)
1961-
static byte * receive_msg(int socket)
2024+
static byte * receive_msg(int socket, size_t *msg_len_out)
19622025
#endif
19632026
{
19642027
byte lengthstr[LENGTH_INDICATOR_SIZE];
@@ -1979,6 +2042,9 @@ static byte * receive_msg(int socket)
19792042
DO_EXIT(EXIT_SOCKET_RECV_BODY);
19802043
}
19812044

2045+
if (msg_len_out != NULL)
2046+
*msg_len_out = msg_len;
2047+
19822048
return buffer;
19832049
}
19842050

lib/odbc/c_src/odbcserver.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,21 @@
2424

2525
/* ----------------------------- CONSTANTS ------------------------------*/
2626

27-
#define MAXCOLSIZE 8001
27+
/* Default ODBC bind buffer size (bytes) for SQL_LONGVARCHAR,
28+
* SQL_LONGVARBINARY, and SQL_WLONGVARCHAR when the driver reports size 0 or an
29+
* oversized display size (e.g. SQL Server varchar(max)). Override per
30+
* connection from Erlang (see max_long_column_size) or with environment
31+
* variable ERL_ODBC_MAX_LONG_COLUMN_SIZE for legacy clients. Larger values
32+
* use more memory per column per bound row. */
33+
#define DEFAULT_MAX_LONG_COL_SIZE 8001
34+
/* Hard upper bound to avoid runaway allocation (256 MiB per column). */
35+
#define HARD_MAX_LONG_COL_SIZE (256U * 1024U * 1024U)
36+
/* Extended OPEN_CONNECTION layout: these two bytes follow the six option
37+
* bytes, then four big-endian size octets, then the connection string.
38+
* (Single-byte 0xFF alone could match a rare first character of a legacy
39+
* connection string.) */
40+
#define OPEN_CONNECTION_PROTO_V1_A 0xFFU
41+
#define OPEN_CONNECTION_PROTO_V1_B 0x01U
2842
#define MAX_ERR_MSG 1024
2943
#define ERRMSG_HEADR_SIZE 20
3044
#define MAX_CONN_STR_OUT 1024
@@ -184,6 +198,7 @@ typedef struct {
184198
Boolean param_query;
185199
Boolean out_params;
186200
Boolean extended_errors;
201+
SQLULEN max_long_column_size;
187202
} db_state;
188203

189204
typedef enum {
@@ -205,3 +220,4 @@ typedef enum {
205220
#define out_params(db_state) (db_state -> out_params)
206221
#define extended_errors(db_state) (db_state -> extended_errors)
207222
#define extended_error(db_state, errorcode) ( extended_errors(state) ? ((char *)errorcode) : NULL )
223+
#define max_long_column_size(db_state) ((db_state)->max_long_column_size)

0 commit comments

Comments
 (0)