Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1reqdD-00DqCS-SM for pgsql-odbc@arkaria.postgresql.org; Tue, 27 Feb 2024 06:08:00 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1reqdC-0018VA-GY for pgsql-odbc@arkaria.postgresql.org; Tue, 27 Feb 2024 06:07:58 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1reqdC-0018Sq-0S for pgsql-odbc@lists.postgresql.org; Tue, 27 Feb 2024 06:07:58 +0000 Received: from mail-lj1-x22e.google.com ([2a00:1450:4864:20::22e]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1reqd3-001PZB-EA for pgsql-odbc@lists.postgresql.org; Tue, 27 Feb 2024 06:07:57 +0000 Received: by mail-lj1-x22e.google.com with SMTP id 38308e7fff4ca-2d21a68dd3bso51732821fa.1 for ; Mon, 26 Feb 2024 22:07:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1709014069; x=1709618869; darn=lists.postgresql.org; h=in-reply-to:content-language:references:cc:to:subject:from :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=Cg40ycFAtunBKrtgWSl82oz58iK/v0UvfDWYZO3Cg3s=; b=Cx3Nv8u+uWEbqtL+dYTjXnmXmy+cg+nNoBp3SD5I6bGROLKkImsgzUBWeglXu94qoL y6l7iEZdq730cOJ4htqUU1PTQpOqou8aC5gNJZXo5Pqj9Mn1VN9wZfLDKtoyGQLHBYZZ ruxA01pWUE6BcAecr7nHk2cOBoAJQeKDHxszF38owcuOIz7Dc+oBh7rssJdwmXUGXvGn VwJMMIIAyWIV/CZAU4XjTYRlMY6zfPUv/+3N3fKaV9wpZDK0jtrPUJrS0YLABenpLXRW 02tP9/SIb/M7XAewSKiI2ekwqGMdkJ7sPAkireAx0IGNOVT9ufybOWzgD5EJW0fmBI48 Nc2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1709014069; x=1709618869; h=in-reply-to:content-language:references:cc:to:subject:from :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=Cg40ycFAtunBKrtgWSl82oz58iK/v0UvfDWYZO3Cg3s=; b=WlA9cCawYYv4e2j6lPmmdjPueYAjXFpSAneNowWWnnVjuPm58Q9GeTp5QTdww7DM+y SMq1qLoJ0OBJDhoKopePT5CnQbWLQDelpHGnseaXfmZ1CZ5M9Q/3P+pom7vZtbeH6j3f wBCn8ffhDccwwzAKz6ZF2+czjhJHLVdTfW5O6YJQyASLFECFpOBPWmLulu+zO0DjeVm3 8YMGlluK23jdSsWg60YI7wjFKbEHlQnmtqdnFWasRaaLngM4bABv90JQzb49fvAE297e vlsB/hrvYiPPgMKAET/izoAvkyaL3NktwRioqieIkqwyT5BIToy3+sqHJ+h8pjPZwPC/ DCkQ== X-Gm-Message-State: AOJu0YxsWVYhw1XI7AwBvtWwk6DtMMpPU8EHqKMY0Rw5rCL7KiPag8pv JmmqSl35SatbU0St55i89sYvQOR5LrGks3nOpiURz0aJo7K3ptaXaY06hLNEysYClQ== X-Google-Smtp-Source: AGHT+IFZzlup4UA5IoAfyW0uMO5eXAA0vIpmp6qSQlvKtz0olVF7mvyQqeR0MouEY5QLJprOW47Rmg== X-Received: by 2002:a2e:b0d2:0:b0:2d2:329f:7a7e with SMTP id g18-20020a2eb0d2000000b002d2329f7a7emr5814078ljl.6.1709014068510; Mon, 26 Feb 2024 22:07:48 -0800 (PST) Received: from [172.16.145.34] (isp-nts.automiq.ru. [92.243.127.242]) by smtp.gmail.com with ESMTPSA id t6-20020a2e7806000000b002d2607e6d29sm1106348ljc.70.2024.02.26.22.07.47 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 26 Feb 2024 22:07:48 -0800 (PST) Content-Type: multipart/alternative; boundary="------------7Vld1JVKvdLM2UUTdyDazMbg" Message-ID: <1ad9d955-445f-4eef-a4cd-83071beba366@gmail.com> Date: Tue, 27 Feb 2024 13:07:47 +0700 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: Andrey Sukhanov Subject: Re: psqlodbc crashes while collecting diagnostic records with SQLGetDiagRecW To: Dave Cramer Cc: pgsql-odbc@lists.postgresql.org References: <112f6883-34e4-4372-8bda-d04f45ee31bf@gmail.com> Content-Language: ru, en-US In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------7Vld1JVKvdLM2UUTdyDazMbg Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hi Dave, Thank you for your time. The problem is caused by signed integer overflow, which leads to attempt to read from unallocated memory. I have downloaded psqlodbc 16 and debug symbols from https://www.postgresql.org/ftp/odbc/versions/. Source code from for psqlodbc 16 from https://git.postgresql.org/git/psqlodbc.git. I compiled the attached program with msvc 14 and msvc 16 compilers (same results). Program should be compiled with defined "UNICODE" to use functions (SQLGetDiagRecW etc.) that accept wchar. On windows 10 attached program crashes inside ER_ReturnError line environ.c:249. Where it tries to memcpy from the buffer "msg" with offset "stapos". "msg" is a valid pointer to "pgerror->__error_msg". "pgerror" also have field "pgerror->errorsize" with value "32690". Offset "stapos" is calculated as "stapos = (RecNumber - 1) * error->recsize;" Here "error" is "pgerror", "recsize" = 99, "RecNumber" = 332. "stapos" should be assigned the value of 32769  but it has type SWORD (short int) (INT16_MAX is 32767). Expression"if (stapos > msglen)" (here "msglen" = 32690) evaluates to "false", because of signed integer overflow.  It should've been "true" instead, and function would've returned SQL_NO_DATA_FOUND. Callstack:      vcruntime140.dll!memcpy() line 438      psqlodbc35w.dll!ER_ReturnError(PG_ErrorInfo * pgerror, short RecNumber, unsigned char * szSqlState, long * pfNativeError, unsigned char * szErrorMsg, short cbErrorMsgMax, short * pcbErrorMsg, unsigned short) line 250      psqlodbc35w.dll!PGAPI_StmtError(void * hstmt, short RecNumber, unsigned char * szSqlState, long * pfNativeError, unsigned char * szErrorMsg, short cbErrorMsgMax, short * pcbErrorMsg, unsigned short) line 1626      psqlodbc35w.dll!PGAPI_GetDiagRec(short) line 57      psqlodbc35w.dll!SQLGetDiagRecW(short fHandleType, void * handle, short iRecord, wchar_t * szSqlState, long * pfNativeError, wchar_t * szErrorMsg, short cbErrorMsgMax, short * pcbErrorMsg) line 234      odbc32.dll!SQLGetDiagRecW()      odbc.pg.exe!CollectDiagRecords(short hndlType, void * hndl) line 30      odbc.pg.exe!main(int argc, char * * argv) line 117 That means, that the problem is caused by signed integer overflow, which in turn causes memcpy to read from unallocated memory. The easiest way to reproduce this problem is to try increasing the number of "raise notice" messages in for loop. If their number gets past certain point, no diagnostic records from SQLGetDiagRec are being returned at all. Then I've decreased the number of these messages until they started appearing again. In my case at this number I am able to reproduce the problem. With further decrease of this number problem doesn't appear anymore. I can't fix this problem myself, because I'm not familiar with the project, and I don't know how to properly test psqlodbc on different platforms. Regards, Andrey 27.02.2024 3:11, Dave Cramer wrote: > Hi Andrey, > > I've tried on PostgreSQL 11 and 15 with no luck to replicate this problem. > > Is it possible for you to debug it and send more information? > > Dave Cramer > www.postgres.rocks > > > On Thu, 22 Feb 2024 at 01:29, Andrey Sukhanov wrote: > > Dear pgsql-odbc developers, > > Windows 10,  psqlodbc 16 (psqlodbc35w.dll), postgresql 11. > Getting certain amount of diagnostic records with SQLGetDiagRecW > crashes > the application with memory access violation. > > Steps to reproduce: > 1. Create procedure: > CREATE OR REPLACE PROCEDURE crashme() > LANGUAGE plpgsql > AS $$ > BEGIN > FOR i IN 1..841 LOOP >          RAISE NOTICE 'msgmsgmsgmsg (%)', i; > END LOOP; > END; $$; > > 2. Use example code in attachments. > 3. Application crashes with memory access violation after calling > SQLGetDiagRecW function, with iRecord = 332. > Application doesn't crash if number of iterations in procedure's for > loop is changed. > Expected outcome: SQLGetDiagRecW would return SQL_NO_DATA when > there's > no more diagnostic records. > > Regards, > Andrey > --------------7Vld1JVKvdLM2UUTdyDazMbg Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi Dave,

Thank you for your time.
The problem is caused by signed integer overflow, which leads to attempt to read from unallocated memory.

I have downloaded psqlodbc 16 and debug symbols from https://www.postgresql.org/ftp/odbc/versions/.
Source code from for psqlodbc 16 from https://git.postgresql.org/git/psqlodbc.git.
I compiled the attached program with msvc 14 and msvc 16 compilers (same results). Program should be compiled with defined "UNICODE" to use functions (SQLGetDiagRecW etc.) that accept wchar.

On windows 10 attached program crashes inside ER_ReturnError line environ.c:249. Where it tries to memcpy from the buffer "msg" with offset "stapos".


"msg" is a valid pointer to "pgerror->__error_msg". "pgerror" also have field "pgerror->errorsize" with value "32690".
Offset "stapos" is calculated as "stapos = (RecNumber - 1) * error->recsize;" 
Here "error" is "pgerror", "recsize" = 99, "RecNumber" = 332.
"stapos" should be assigned the value of 32769  but it has type SWORD (short int) (INT16_MAX is 32767).
Expression"if (stapos > msglen)" (here "msglen" = 32690) evaluates to "false", because of signed integer overflow.  It should've been "true" instead, and function would've returned SQL_NO_DATA_FOUND.
Callstack:
     vcruntime140.dll!memcpy() line 438
     psqlodbc35w.dll!ER_ReturnError(PG_ErrorInfo * pgerror, short RecNumber, unsigned char * szSqlState, long * pfNativeError, unsigned char * szErrorMsg, short cbErrorMsgMax, short * pcbErrorMsg, unsigned short) line 250
     psqlodbc35w.dll!PGAPI_StmtError(void * hstmt, short RecNumber, unsigned char * szSqlState, long * pfNativeError, unsigned char * szErrorMsg, short cbErrorMsgMax, short * pcbErrorMsg, unsigned short) line 1626
     psqlodbc35w.dll!PGAPI_GetDiagRec(short) line 57
     psqlodbc35w.dll!SQLGetDiagRecW(short fHandleType, void * handle, short iRecord, wchar_t * szSqlState, long * pfNativeError, wchar_t * szErrorMsg, short cbErrorMsgMax, short * pcbErrorMsg) line 234
     odbc32.dll!SQLGetDiagRecW()
     odbc.pg.exe!CollectDiagRecords(short hndlType, void * hndl) line 30
     odbc.pg.exe!main(int argc, char * * argv) line 117

That means, that the problem is caused by signed integer overflow, which in turn causes memcpy to read from unallocated memory.
The easiest way to reproduce this problem is to try increasing the number of "raise notice" messages in for loop. If their number gets past certain point, no diagnostic records from SQLGetDiagRec are being returned at all. Then I've decreased the number of these messages until they started appearing again. In my case at this number I am able to reproduce the problem. With further decrease of this number problem doesn't appear anymore.

I can't fix this problem myself, because I'm not familiar with the project, and I don't know how to properly test psqlodbc on different platforms.

Regards,
Andrey


27.02.2024 3:11, Dave Cramer wrote:
Hi Andrey,

I've tried on PostgreSQL 11 and 15 with no luck to replicate this problem.

Is it possible for you to debug it and send more information?

Dave Cramer


On Thu, 22 Feb 2024 at 01:29, Andrey Sukhanov <siwenter@gmail.com> wrote:
Dear pgsql-odbc developers,

Windows 10,  psqlodbc 16 (psqlodbc35w.dll), postgresql 11.
Getting certain amount of diagnostic records with SQLGetDiagRecW crashes
the application with memory access violation.

Steps to reproduce:
1. Create procedure:
CREATE OR REPLACE PROCEDURE crashme()
LANGUAGE plpgsql
AS $$
BEGIN
FOR i IN 1..841 LOOP
         RAISE NOTICE 'msgmsgmsgmsg (%)', i;
END LOOP;
END; $$;

2. Use example code in attachments.
3. Application crashes with memory access violation after calling
SQLGetDiagRecW function, with iRecord = 332.
Application doesn't crash if number of iterations in  procedure's for
loop is changed.
Expected outcome: SQLGetDiagRecW would return SQL_NO_DATA when there's
no more diagnostic records.

Regards,
Andrey
--------------7Vld1JVKvdLM2UUTdyDazMbg--