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

Commit600086f

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 parent3e0ae46 commit600086f

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>
@@ -1140,7 +1141,7 @@ parse_comma_separated_list(char **startptr, bool *more)
11401141
char*p;
11411142
char*s=*startptr;
11421143
char*e;
1143-
intlen;
1144+
size_tlen;
11441145

11451146
/*
11461147
* Search for the end of the current element; a comma or end-of-string
@@ -5769,7 +5770,21 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options,
57695770
/* concatenate values into a single string with newline terminators */
57705771
size=1;/* for the trailing null */
57715772
for (i=0;values[i]!=NULL;i++)
5773+
{
5774+
if (values[i]->bv_len >=INT_MAX||
5775+
size> (INT_MAX- (values[i]->bv_len+1)))
5776+
{
5777+
libpq_append_error(errorMessage,
5778+
"connection info string size exceeds the maximum allowed (%d)",
5779+
INT_MAX);
5780+
ldap_value_free_len(values);
5781+
ldap_unbind(ld);
5782+
return3;
5783+
}
5784+
57725785
size+=values[i]->bv_len+1;
5786+
}
5787+
57735788
if ((result=malloc(size))==NULL)
57745789
{
57755790
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;
@@ -1274,7 +1279,7 @@ pqRowProcessor(PGconn *conn, const char **errmsgp)
12741279
boolisbinary= (res->attDescs[i].format!=0);
12751280
char*val;
12761281

1277-
val= (char*)pqResultAlloc(res,clen+1,isbinary);
1282+
val= (char*)pqResultAlloc(res,(size_t)clen+1,isbinary);
12781283
if (val==NULL)
12791284
return0;
12801285

@@ -4215,6 +4220,27 @@ PQescapeString(char *to, const char *from, size_t length)
42154220
}
42164221

42174222

4223+
/*
4224+
* Frontend version of the backend's add_size(), intended to be API-compatible
4225+
* with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
4226+
* Returns true instead if the addition overflows.
4227+
*
4228+
* TODO: move to common/int.h
4229+
*/
4230+
staticbool
4231+
add_size_overflow(size_ts1,size_ts2,size_t*dst)
4232+
{
4233+
size_tresult;
4234+
4235+
result=s1+s2;
4236+
if (result<s1||result<s2)
4237+
return true;
4238+
4239+
*dst=result;
4240+
return false;
4241+
}
4242+
4243+
42184244
/*
42194245
* Escape arbitrary strings. If as_ident is true, we escape the result
42204246
* as an identifier; if false, as a literal. The result is returned in
@@ -4227,8 +4253,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
42274253
constchar*s;
42284254
char*result;
42294255
char*rp;
4230-
intnum_quotes=0;/* single or double, depending on as_ident */
4231-
intnum_backslashes=0;
4256+
size_tnum_quotes=0;/* single or double, depending on as_ident */
4257+
size_tnum_backslashes=0;
42324258
size_tinput_len=strnlen(str,len);
42334259
size_tresult_size;
42344260
charquote_char=as_ident ?'"' :'\'';
@@ -4294,10 +4320,21 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
42944320
}
42954321
}
42964322

4297-
/* Allocate output buffer. */
4298-
result_size=input_len+num_quotes+3;/* two quotes, plus a NUL */
4323+
/*
4324+
* Allocate output buffer. Protect against overflow, in case the caller
4325+
* has allocated a large fraction of the available size_t.
4326+
*/
4327+
if (add_size_overflow(input_len,num_quotes,&result_size)||
4328+
add_size_overflow(result_size,3,&result_size))/* two quotes plus a NUL */
4329+
gotooverflow;
4330+
42994331
if (!as_ident&&num_backslashes>0)
4300-
result_size+=num_backslashes+2;
4332+
{
4333+
if (add_size_overflow(result_size,num_backslashes,&result_size)||
4334+
add_size_overflow(result_size,2,&result_size))/* for " E" prefix */
4335+
gotooverflow;
4336+
}
4337+
43014338
result=rp= (char*)malloc(result_size);
43024339
if (rp==NULL)
43034340
{
@@ -4370,6 +4407,12 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
43704407
*rp='\0';
43714408

43724409
returnresult;
4410+
4411+
overflow:
4412+
libpq_append_conn_error(conn,
4413+
"escaped string size exceeds the maximum allowed (%zu)",
4414+
SIZE_MAX);
4415+
returnNULL;
43734416
}
43744417

43754418
char*
@@ -4435,30 +4478,51 @@ PQescapeByteaInternal(PGconn *conn,
44354478
unsignedchar*result;
44364479
size_ti;
44374480
size_tlen;
4438-
size_tbslash_len= (std_strings ?1 :2);
4481+
constsize_tbslash_len= (std_strings ?1 :2);
44394482

44404483
/*
4441-
* empty string has 1 char ('\0')
4484+
* Calculate the escaped length, watching for overflow as we do with
4485+
* PQescapeInternal(). The following code relies on a small constant
4486+
* bslash_len so that small additions and multiplications don't need their
4487+
* own overflow checks.
4488+
*
4489+
* Start with the empty string, which has 1 char ('\0').
44424490
*/
44434491
len=1;
44444492

44454493
if (use_hex)
44464494
{
4447-
len+=bslash_len+1+2*from_length;
4495+
/* We prepend "\x" and double each input character. */
4496+
if (add_size_overflow(len,bslash_len+1,&len)||
4497+
add_size_overflow(len,from_length,&len)||
4498+
add_size_overflow(len,from_length,&len))
4499+
gotooverflow;
44484500
}
44494501
else
44504502
{
44514503
vp=from;
44524504
for (i=from_length;i>0;i--,vp++)
44534505
{
44544506
if (*vp<0x20||*vp>0x7e)
4455-
len+=bslash_len+3;
4507+
{
4508+
if (add_size_overflow(len,bslash_len+3,&len))/* octal "\ooo" */
4509+
gotooverflow;
4510+
}
44564511
elseif (*vp=='\'')
4457-
len+=2;
4512+
{
4513+
if (add_size_overflow(len,2,&len))/* double each quote */
4514+
gotooverflow;
4515+
}
44584516
elseif (*vp=='\\')
4459-
len+=bslash_len+bslash_len;
4517+
{
4518+
if (add_size_overflow(len,bslash_len*2,&len))/* double each backslash */
4519+
gotooverflow;
4520+
}
44604521
else
4461-
len++;
4522+
{
4523+
if (add_size_overflow(len,1,&len))
4524+
gotooverflow;
4525+
}
44624526
}
44634527
}
44644528

@@ -4519,6 +4583,13 @@ PQescapeByteaInternal(PGconn *conn,
45194583
*rp='\0';
45204584

45214585
returnresult;
4586+
4587+
overflow:
4588+
if (conn)
4589+
libpq_append_conn_error(conn,
4590+
"escaped bytea size exceeds the maximum allowed (%zu)",
4591+
SIZE_MAX);
4592+
returnNULL;
45224593
}
45234594

45244595
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