Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf5999f0

Browse files
committed
libpq: Prevent some overflows of int/size_t
Several functions could overflow their size calculations, when presentedwith very large inputs from remote and/or untrusted locations, and thenallocate buffers that were too small to hold the intended contents.Switch from int to size_t where appropriate, and check for overflowconditions when the inputs could have plausibly originated outside ofthe libpq trust boundary. (Overflows from within the trust boundary arestill possible, but these will be fixed separately.) A version ofadd_size() is ported from the backend to assist with code that performsmore complicated concatenation.Reported-by: Aleksey Solovev (Positive Technologies)Reviewed-by: Noah Misch <noah@leadboat.com>Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de>Security:CVE-2025-12818Backpatch-through: 13
1 parent3b5a61d commitf5999f0

File tree

5 files changed

+224
-33
lines changed

5 files changed

+224
-33
lines changed

‎src/interfaces/libpq/fe-connect.c‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include<sys/stat.h>
1919
#include<fcntl.h>
2020
#include<ctype.h>
21+
#include<limits.h>
2122
#include<netdb.h>
2223
#include<time.h>
2324
#include<unistd.h>
@@ -1060,7 +1061,7 @@ parse_comma_separated_list(char **startptr, bool *more)
10601061
char*p;
10611062
char*s=*startptr;
10621063
char*e;
1063-
intlen;
1064+
size_tlen;
10641065

10651066
/*
10661067
* Search for the end of the current element; a comma or end-of-string
@@ -5322,7 +5323,21 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
53225323
/* concatenate values into a single string with newline terminators */
53235324
size=1;/* for the trailing null */
53245325
for (i=0;values[i]!=NULL;i++)
5326+
{
5327+
if (values[i]->bv_len >=INT_MAX||
5328+
size> (INT_MAX- (values[i]->bv_len+1)))
5329+
{
5330+
libpq_append_error(errorMessage,
5331+
"connection info string size exceeds the maximum allowed (%d)",
5332+
INT_MAX);
5333+
ldap_value_free_len(values);
5334+
ldap_unbind(ld);
5335+
return3;
5336+
}
5337+
53255338
size+=values[i]->bv_len+1;
5339+
}
5340+
53265341
if ((result=malloc(size))==NULL)
53275342
{
53285343
libpq_append_error(errorMessage,"out of memory");

‎src/interfaces/libpq/fe-exec.c‎

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
511511
}
512512
else
513513
{
514-
attval->value= (char*)pqResultAlloc(res,len+1, true);
514+
attval->value= (char*)pqResultAlloc(res,(size_t)len+1, true);
515515
if (!attval->value)
516516
gotofail;
517517
attval->len=len;
@@ -603,8 +603,13 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
603603
*/
604604
if (nBytes >=PGRESULT_SEP_ALLOC_THRESHOLD)
605605
{
606-
size_talloc_size=nBytes+PGRESULT_BLOCK_OVERHEAD;
606+
size_talloc_size;
607607

608+
/* Don't wrap around with overly large requests. */
609+
if (nBytes>SIZE_MAX-PGRESULT_BLOCK_OVERHEAD)
610+
returnNULL;
611+
612+
alloc_size=nBytes+PGRESULT_BLOCK_OVERHEAD;
608613
block= (PGresult_data*)malloc(alloc_size);
609614
if (!block)
610615
returnNULL;
@@ -1263,7 +1268,7 @@ pqRowProcessor(PGconn *conn, const char **errmsgp)
12631268
boolisbinary= (res->attDescs[i].format!=0);
12641269
char*val;
12651270

1266-
val= (char*)pqResultAlloc(res,clen+1,isbinary);
1271+
val= (char*)pqResultAlloc(res,(size_t)clen+1,isbinary);
12671272
if (val==NULL)
12681273
return0;
12691274

@@ -4204,6 +4209,27 @@ PQescapeString(char *to, const char *from, size_t length)
42044209
}
42054210

42064211

4212+
/*
4213+
* Frontend version of the backend's add_size(), intended to be API-compatible
4214+
* with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
4215+
* Returns true instead if the addition overflows.
4216+
*
4217+
* TODO: move to common/int.h
4218+
*/
4219+
staticbool
4220+
add_size_overflow(size_ts1,size_ts2,size_t*dst)
4221+
{
4222+
size_tresult;
4223+
4224+
result=s1+s2;
4225+
if (result<s1||result<s2)
4226+
return true;
4227+
4228+
*dst=result;
4229+
return false;
4230+
}
4231+
4232+
42074233
/*
42084234
* Escape arbitrary strings. If as_ident is true, we escape the result
42094235
* as an identifier; if false, as a literal. The result is returned in
@@ -4216,8 +4242,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
42164242
constchar*s;
42174243
char*result;
42184244
char*rp;
4219-
intnum_quotes=0;/* single or double, depending on as_ident */
4220-
intnum_backslashes=0;
4245+
size_tnum_quotes=0;/* single or double, depending on as_ident */
4246+
size_tnum_backslashes=0;
42214247
size_tinput_len=strnlen(str,len);
42224248
size_tresult_size;
42234249
charquote_char=as_ident ?'"' :'\'';
@@ -4283,10 +4309,21 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
42834309
}
42844310
}
42854311

4286-
/* Allocate output buffer. */
4287-
result_size=input_len+num_quotes+3;/* two quotes, plus a NUL */
4312+
/*
4313+
* Allocate output buffer. Protect against overflow, in case the caller
4314+
* has allocated a large fraction of the available size_t.
4315+
*/
4316+
if (add_size_overflow(input_len,num_quotes,&result_size)||
4317+
add_size_overflow(result_size,3,&result_size))/* two quotes plus a NUL */
4318+
gotooverflow;
4319+
42884320
if (!as_ident&&num_backslashes>0)
4289-
result_size+=num_backslashes+2;
4321+
{
4322+
if (add_size_overflow(result_size,num_backslashes,&result_size)||
4323+
add_size_overflow(result_size,2,&result_size))/* for " E" prefix */
4324+
gotooverflow;
4325+
}
4326+
42904327
result=rp= (char*)malloc(result_size);
42914328
if (rp==NULL)
42924329
{
@@ -4359,6 +4396,12 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
43594396
*rp='\0';
43604397

43614398
returnresult;
4399+
4400+
overflow:
4401+
libpq_append_conn_error(conn,
4402+
"escaped string size exceeds the maximum allowed (%zu)",
4403+
SIZE_MAX);
4404+
returnNULL;
43624405
}
43634406

43644407
char*
@@ -4424,30 +4467,51 @@ PQescapeByteaInternal(PGconn *conn,
44244467
unsignedchar*result;
44254468
size_ti;
44264469
size_tlen;
4427-
size_tbslash_len= (std_strings ?1 :2);
4470+
constsize_tbslash_len= (std_strings ?1 :2);
44284471

44294472
/*
4430-
* empty string has 1 char ('\0')
4473+
* Calculate the escaped length, watching for overflow as we do with
4474+
* PQescapeInternal(). The following code relies on a small constant
4475+
* bslash_len so that small additions and multiplications don't need their
4476+
* own overflow checks.
4477+
*
4478+
* Start with the empty string, which has 1 char ('\0').
44314479
*/
44324480
len=1;
44334481

44344482
if (use_hex)
44354483
{
4436-
len+=bslash_len+1+2*from_length;
4484+
/* We prepend "\x" and double each input character. */
4485+
if (add_size_overflow(len,bslash_len+1,&len)||
4486+
add_size_overflow(len,from_length,&len)||
4487+
add_size_overflow(len,from_length,&len))
4488+
gotooverflow;
44374489
}
44384490
else
44394491
{
44404492
vp=from;
44414493
for (i=from_length;i>0;i--,vp++)
44424494
{
44434495
if (*vp<0x20||*vp>0x7e)
4444-
len+=bslash_len+3;
4496+
{
4497+
if (add_size_overflow(len,bslash_len+3,&len))/* octal "\ooo" */
4498+
gotooverflow;
4499+
}
44454500
elseif (*vp=='\'')
4446-
len+=2;
4501+
{
4502+
if (add_size_overflow(len,2,&len))/* double each quote */
4503+
gotooverflow;
4504+
}
44474505
elseif (*vp=='\\')
4448-
len+=bslash_len+bslash_len;
4506+
{
4507+
if (add_size_overflow(len,bslash_len*2,&len))/* double each backslash */
4508+
gotooverflow;
4509+
}
44494510
else
4450-
len++;
4511+
{
4512+
if (add_size_overflow(len,1,&len))
4513+
gotooverflow;
4514+
}
44514515
}
44524516
}
44534517

@@ -4508,6 +4572,13 @@ PQescapeByteaInternal(PGconn *conn,
45084572
*rp='\0';
45094573

45104574
returnresult;
4575+
4576+
overflow:
4577+
if (conn)
4578+
libpq_append_conn_error(conn,
4579+
"escaped bytea size exceeds the maximum allowed (%zu)",
4580+
SIZE_MAX);
4581+
returnNULL;
45114582
}
45124583

45134584
unsignedchar*

‎src/interfaces/libpq/fe-print.c‎

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
104104
}screen_size;
105105
#endif
106106

107+
/*
108+
* Quick sanity check on po->fieldSep, since we make heavy use of int
109+
* math throughout.
110+
*/
111+
if (fs_len<strlen(po->fieldSep))
112+
{
113+
fprintf(stderr,libpq_gettext("overlong field separator\n"));
114+
gotoexit;
115+
}
116+
107117
nTups=PQntuples(res);
108118
fieldNames= (constchar**)calloc(nFields,sizeof(char*));
109119
fieldNotNum= (unsignedchar*)calloc(nFields,1);
@@ -391,7 +401,7 @@ do_field(const PQprintOpt *po, const PGresult *res,
391401
{
392402
if (plen>fieldMax[j])
393403
fieldMax[j]=plen;
394-
if (!(fields[i*nFields+j]= (char*)malloc(plen+1)))
404+
if (!(fields[i*nFields+j]= (char*)malloc((size_t)plen+1)))
395405
{
396406
fprintf(stderr,libpq_gettext("out of memory\n"));
397407
return false;
@@ -441,6 +451,27 @@ do_field(const PQprintOpt *po, const PGresult *res,
441451
}
442452

443453

454+
/*
455+
* Frontend version of the backend's add_size(), intended to be API-compatible
456+
* with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
457+
* Returns true instead if the addition overflows.
458+
*
459+
* TODO: move to common/int.h
460+
*/
461+
staticbool
462+
add_size_overflow(size_ts1,size_ts2,size_t*dst)
463+
{
464+
size_tresult;
465+
466+
result=s1+s2;
467+
if (result<s1||result<s2)
468+
return true;
469+
470+
*dst=result;
471+
return false;
472+
}
473+
474+
444475
staticchar*
445476
do_header(FILE*fout,constPQprintOpt*po,constintnFields,int*fieldMax,
446477
constchar**fieldNames,unsignedchar*fieldNotNum,
@@ -453,15 +484,31 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
453484
fputs("<tr>",fout);
454485
else
455486
{
456-
inttot=0;
487+
size_ttot=0;
457488
intn=0;
458489
char*p=NULL;
459490

491+
/* Calculate the border size, checking for overflow. */
460492
for (;n<nFields;n++)
461-
tot+=fieldMax[n]+fs_len+ (po->standard ?2 :0);
493+
{
494+
/* Field plus separator, plus 2 extra '-' in standard format. */
495+
if (add_size_overflow(tot,fieldMax[n],&tot)||
496+
add_size_overflow(tot,fs_len,&tot)||
497+
(po->standard&&add_size_overflow(tot,2,&tot)))
498+
gotooverflow;
499+
}
462500
if (po->standard)
463-
tot+=fs_len*2+2;
464-
border=malloc(tot+1);
501+
{
502+
/* An extra separator at the front and back. */
503+
if (add_size_overflow(tot,fs_len,&tot)||
504+
add_size_overflow(tot,fs_len,&tot)||
505+
add_size_overflow(tot,2,&tot))
506+
gotooverflow;
507+
}
508+
if (add_size_overflow(tot,1,&tot))/* terminator */
509+
gotooverflow;
510+
511+
border=malloc(tot);
465512
if (!border)
466513
{
467514
fprintf(stderr,libpq_gettext("out of memory\n"));
@@ -524,6 +571,10 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
524571
else
525572
fprintf(fout,"\n%s\n",border);
526573
returnborder;
574+
575+
overflow:
576+
fprintf(stderr,libpq_gettext("header size exceeds the maximum allowed\n"));
577+
returnNULL;
527578
}
528579

529580

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp