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

Commit96d2c7e

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 parente792be6 commit96d2c7e

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<time.h>
2223
#include<unistd.h>
2324

@@ -998,7 +999,7 @@ parse_comma_separated_list(char **startptr, bool *more)
998999
char*p;
9991000
char*s=*startptr;
10001001
char*e;
1001-
intlen;
1002+
size_tlen;
10021003

10031004
/*
10041005
* Search for the end of the current element; a comma or end-of-string
@@ -4968,7 +4969,21 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
49684969
/* concatenate values into a single string with newline terminators */
49694970
size=1;/* for the trailing null */
49704971
for (i=0;values[i]!=NULL;i++)
4972+
{
4973+
if (values[i]->bv_len >=INT_MAX||
4974+
size> (INT_MAX- (values[i]->bv_len+1)))
4975+
{
4976+
appendPQExpBuffer(errorMessage,
4977+
libpq_gettext("connection info string size exceeds the maximum allowed (%d)\n"),
4978+
INT_MAX);
4979+
ldap_value_free_len(values);
4980+
ldap_unbind(ld);
4981+
return3;
4982+
}
4983+
49714984
size+=values[i]->bv_len+1;
4985+
}
4986+
49724987
if ((result=malloc(size))==NULL)
49734988
{
49744989
appendPQExpBufferStr(errorMessage,

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

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
491491
}
492492
else
493493
{
494-
attval->value= (char*)pqResultAlloc(res,len+1, true);
494+
attval->value= (char*)pqResultAlloc(res,(size_t)len+1, true);
495495
if (!attval->value)
496496
gotofail;
497497
attval->len=len;
@@ -579,8 +579,13 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
579579
*/
580580
if (nBytes >=PGRESULT_SEP_ALLOC_THRESHOLD)
581581
{
582-
size_talloc_size=nBytes+PGRESULT_BLOCK_OVERHEAD;
582+
size_talloc_size;
583583

584+
/* Don't wrap around with overly large requests. */
585+
if (nBytes>SIZE_MAX-PGRESULT_BLOCK_OVERHEAD)
586+
returnNULL;
587+
588+
alloc_size=nBytes+PGRESULT_BLOCK_OVERHEAD;
584589
block= (PGresult_data*)malloc(alloc_size);
585590
if (!block)
586591
returnNULL;
@@ -1156,7 +1161,7 @@ pqRowProcessor(PGconn *conn, const char **errmsgp)
11561161
boolisbinary= (res->attDescs[i].format!=0);
11571162
char*val;
11581163

1159-
val= (char*)pqResultAlloc(res,clen+1,isbinary);
1164+
val= (char*)pqResultAlloc(res,(size_t)clen+1,isbinary);
11601165
if (val==NULL)
11611166
gotofail;
11621167

@@ -4084,6 +4089,27 @@ PQescapeString(char *to, const char *from, size_t length)
40844089
}
40854090

40864091

4092+
/*
4093+
* Frontend version of the backend's add_size(), intended to be API-compatible
4094+
* with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
4095+
* Returns true instead if the addition overflows.
4096+
*
4097+
* TODO: move to common/int.h
4098+
*/
4099+
staticbool
4100+
add_size_overflow(size_ts1,size_ts2,size_t*dst)
4101+
{
4102+
size_tresult;
4103+
4104+
result=s1+s2;
4105+
if (result<s1||result<s2)
4106+
return true;
4107+
4108+
*dst=result;
4109+
return false;
4110+
}
4111+
4112+
40874113
/*
40884114
* Escape arbitrary strings. If as_ident is true, we escape the result
40894115
* as an identifier; if false, as a literal. The result is returned in
@@ -4096,8 +4122,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
40964122
constchar*s;
40974123
char*result;
40984124
char*rp;
4099-
intnum_quotes=0;/* single or double, depending on as_ident */
4100-
intnum_backslashes=0;
4125+
size_tnum_quotes=0;/* single or double, depending on as_ident */
4126+
size_tnum_backslashes=0;
41014127
size_tinput_len=strnlen(str,len);
41024128
size_tresult_size;
41034129
charquote_char=as_ident ?'"' :'\'';
@@ -4163,10 +4189,21 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
41634189
}
41644190
}
41654191

4166-
/* Allocate output buffer. */
4167-
result_size=input_len+num_quotes+3;/* two quotes, plus a NUL */
4192+
/*
4193+
* Allocate output buffer. Protect against overflow, in case the caller
4194+
* has allocated a large fraction of the available size_t.
4195+
*/
4196+
if (add_size_overflow(input_len,num_quotes,&result_size)||
4197+
add_size_overflow(result_size,3,&result_size))/* two quotes plus a NUL */
4198+
gotooverflow;
4199+
41684200
if (!as_ident&&num_backslashes>0)
4169-
result_size+=num_backslashes+2;
4201+
{
4202+
if (add_size_overflow(result_size,num_backslashes,&result_size)||
4203+
add_size_overflow(result_size,2,&result_size))/* for " E" prefix */
4204+
gotooverflow;
4205+
}
4206+
41704207
result=rp= (char*)malloc(result_size);
41714208
if (rp==NULL)
41724209
{
@@ -4240,6 +4277,12 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
42404277
*rp='\0';
42414278

42424279
returnresult;
4280+
4281+
overflow:
4282+
appendPQExpBuffer(&conn->errorMessage,
4283+
libpq_gettext("escaped string size exceeds the maximum allowed (%zu)\n"),
4284+
SIZE_MAX);
4285+
returnNULL;
42434286
}
42444287

42454288
char*
@@ -4305,30 +4348,51 @@ PQescapeByteaInternal(PGconn *conn,
43054348
unsignedchar*result;
43064349
size_ti;
43074350
size_tlen;
4308-
size_tbslash_len= (std_strings ?1 :2);
4351+
constsize_tbslash_len= (std_strings ?1 :2);
43094352

43104353
/*
4311-
* empty string has 1 char ('\0')
4354+
* Calculate the escaped length, watching for overflow as we do with
4355+
* PQescapeInternal(). The following code relies on a small constant
4356+
* bslash_len so that small additions and multiplications don't need their
4357+
* own overflow checks.
4358+
*
4359+
* Start with the empty string, which has 1 char ('\0').
43124360
*/
43134361
len=1;
43144362

43154363
if (use_hex)
43164364
{
4317-
len+=bslash_len+1+2*from_length;
4365+
/* We prepend "\x" and double each input character. */
4366+
if (add_size_overflow(len,bslash_len+1,&len)||
4367+
add_size_overflow(len,from_length,&len)||
4368+
add_size_overflow(len,from_length,&len))
4369+
gotooverflow;
43184370
}
43194371
else
43204372
{
43214373
vp=from;
43224374
for (i=from_length;i>0;i--,vp++)
43234375
{
43244376
if (*vp<0x20||*vp>0x7e)
4325-
len+=bslash_len+3;
4377+
{
4378+
if (add_size_overflow(len,bslash_len+3,&len))/* octal "\ooo" */
4379+
gotooverflow;
4380+
}
43264381
elseif (*vp=='\'')
4327-
len+=2;
4382+
{
4383+
if (add_size_overflow(len,2,&len))/* double each quote */
4384+
gotooverflow;
4385+
}
43284386
elseif (*vp=='\\')
4329-
len+=bslash_len+bslash_len;
4387+
{
4388+
if (add_size_overflow(len,bslash_len*2,&len))/* double each backslash */
4389+
gotooverflow;
4390+
}
43304391
else
4331-
len++;
4392+
{
4393+
if (add_size_overflow(len,1,&len))
4394+
gotooverflow;
4395+
}
43324396
}
43334397
}
43344398

@@ -4390,6 +4454,13 @@ PQescapeByteaInternal(PGconn *conn,
43904454
*rp='\0';
43914455

43924456
returnresult;
4457+
4458+
overflow:
4459+
if (conn)
4460+
appendPQExpBuffer(&conn->errorMessage,
4461+
libpq_gettext("escaped bytea size exceeds the maximum allowed (%zu)\n"),
4462+
SIZE_MAX);
4463+
returnNULL;
43934464
}
43944465

43954466
unsignedchar*

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

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

110+
/*
111+
* Quick sanity check on po->fieldSep, since we make heavy use of int
112+
* math throughout.
113+
*/
114+
if (fs_len<strlen(po->fieldSep))
115+
{
116+
fprintf(stderr,libpq_gettext("overlong field separator\n"));
117+
gotoexit;
118+
}
119+
110120
nTups=PQntuples(res);
111121
fieldNames= (constchar**)calloc(nFields,sizeof(char*));
112122
fieldNotNum= (unsignedchar*)calloc(nFields,1);
@@ -408,7 +418,7 @@ do_field(const PQprintOpt *po, const PGresult *res,
408418
{
409419
if (plen>fieldMax[j])
410420
fieldMax[j]=plen;
411-
if (!(fields[i*nFields+j]= (char*)malloc(plen+1)))
421+
if (!(fields[i*nFields+j]= (char*)malloc((size_t)plen+1)))
412422
{
413423
fprintf(stderr,libpq_gettext("out of memory\n"));
414424
return false;
@@ -458,6 +468,27 @@ do_field(const PQprintOpt *po, const PGresult *res,
458468
}
459469

460470

471+
/*
472+
* Frontend version of the backend's add_size(), intended to be API-compatible
473+
* with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
474+
* Returns true instead if the addition overflows.
475+
*
476+
* TODO: move to common/int.h
477+
*/
478+
staticbool
479+
add_size_overflow(size_ts1,size_ts2,size_t*dst)
480+
{
481+
size_tresult;
482+
483+
result=s1+s2;
484+
if (result<s1||result<s2)
485+
return true;
486+
487+
*dst=result;
488+
return false;
489+
}
490+
491+
461492
staticchar*
462493
do_header(FILE*fout,constPQprintOpt*po,constintnFields,int*fieldMax,
463494
constchar**fieldNames,unsignedchar*fieldNotNum,
@@ -470,15 +501,31 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
470501
fputs("<tr>",fout);
471502
else
472503
{
473-
inttot=0;
504+
size_ttot=0;
474505
intn=0;
475506
char*p=NULL;
476507

508+
/* Calculate the border size, checking for overflow. */
477509
for (;n<nFields;n++)
478-
tot+=fieldMax[n]+fs_len+ (po->standard ?2 :0);
510+
{
511+
/* Field plus separator, plus 2 extra '-' in standard format. */
512+
if (add_size_overflow(tot,fieldMax[n],&tot)||
513+
add_size_overflow(tot,fs_len,&tot)||
514+
(po->standard&&add_size_overflow(tot,2,&tot)))
515+
gotooverflow;
516+
}
479517
if (po->standard)
480-
tot+=fs_len*2+2;
481-
border=malloc(tot+1);
518+
{
519+
/* An extra separator at the front and back. */
520+
if (add_size_overflow(tot,fs_len,&tot)||
521+
add_size_overflow(tot,fs_len,&tot)||
522+
add_size_overflow(tot,2,&tot))
523+
gotooverflow;
524+
}
525+
if (add_size_overflow(tot,1,&tot))/* terminator */
526+
gotooverflow;
527+
528+
border=malloc(tot);
482529
if (!border)
483530
{
484531
fprintf(stderr,libpq_gettext("out of memory\n"));
@@ -541,6 +588,10 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
541588
else
542589
fprintf(fout,"\n%s\n",border);
543590
returnborder;
591+
592+
overflow:
593+
fprintf(stderr,libpq_gettext("header size exceeds the maximum allowed\n"));
594+
returnNULL;
544595
}
545596

546597

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp