Skip to content

Commit 2fefae4

Browse files
committed
Fixed Sec Bug #67717 segfault in dns_get_record CVE-2014-3597
Incomplete fix for CVE-2014-4049 Check possible buffer overflow - pass real buffer end to dn_expand calls - check buffer len before each read
1 parent 1504f7d commit 2fefae4

File tree

1 file changed

+60
-24
lines changed

1 file changed

+60
-24
lines changed

ext/standard/dns.c

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,14 @@ PHP_FUNCTION(dns_check_record)
412412

413413
#if HAVE_FULL_DNS_FUNCS
414414

415+
#define CHECKCP(n) do { \
416+
if (cp + n > end) { \
417+
return NULL; \
418+
} \
419+
} while (0)
420+
415421
/* {{{ php_parserr */
416-
static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int store, int raw, zval **subarray)
422+
static u_char *php_parserr(u_char *cp, u_char *end, querybuf *answer, int type_to_fetch, int store, int raw, zval **subarray)
417423
{
418424
u_short type, class, dlen;
419425
u_long ttl;
@@ -425,16 +431,18 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
425431

426432
*subarray = NULL;
427433

428-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2);
434+
n = dn_expand(answer->qb2, end, cp, name, sizeof(name) - 2);
429435
if (n < 0) {
430436
return NULL;
431437
}
432438
cp += n;
433439

440+
CHECKCP(10);
434441
GETSHORT(type, cp);
435442
GETSHORT(class, cp);
436443
GETLONG(ttl, cp);
437444
GETSHORT(dlen, cp);
445+
CHECKCP(dlen);
438446
if (type_to_fetch != T_ANY && type != type_to_fetch) {
439447
cp += dlen;
440448
return cp;
@@ -461,12 +469,14 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
461469

462470
switch (type) {
463471
case DNS_T_A:
472+
CHECKCP(4);
464473
add_assoc_string(*subarray, "type", "A", 1);
465474
snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
466475
add_assoc_string(*subarray, "ip", name, 1);
467476
cp += dlen;
468477
break;
469478
case DNS_T_MX:
479+
CHECKCP(2);
470480
add_assoc_string(*subarray, "type", "MX", 1);
471481
GETSHORT(n, cp);
472482
add_assoc_long(*subarray, "pri", n);
@@ -485,7 +495,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
485495
if (type == DNS_T_PTR) {
486496
add_assoc_string(*subarray, "type", "PTR", 1);
487497
}
488-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
498+
n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
489499
if (n < 0) {
490500
return NULL;
491501
}
@@ -495,18 +505,22 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
495505
case DNS_T_HINFO:
496506
/* See RFC 1010 for values */
497507
add_assoc_string(*subarray, "type", "HINFO", 1);
508+
CHECKCP(1);
498509
n = *cp & 0xFF;
499510
cp++;
511+
CHECKCP(n);
500512
add_assoc_stringl(*subarray, "cpu", (char*)cp, n, 1);
501513
cp += n;
514+
CHECKCP(1);
502515
n = *cp & 0xFF;
503516
cp++;
517+
CHECKCP(n);
504518
add_assoc_stringl(*subarray, "os", (char*)cp, n, 1);
505519
cp += n;
506520
break;
507521
case DNS_T_TXT:
508522
{
509-
int ll = 0;
523+
int l1 = 0, l2 = 0;
510524
zval *entries = NULL;
511525

512526
add_assoc_string(*subarray, "type", "TXT", 1);
@@ -515,37 +529,41 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
515529
MAKE_STD_ZVAL(entries);
516530
array_init(entries);
517531

518-
while (ll < dlen) {
519-
n = cp[ll];
520-
if ((ll + n) >= dlen) {
532+
while (l1 < dlen) {
533+
n = cp[l1];
534+
if ((l1 + n) >= dlen) {
521535
// Invalid chunk length, truncate
522-
n = dlen - (ll + 1);
536+
n = dlen - (l1 + 1);
537+
}
538+
if (n) {
539+
memcpy(tp + l2 , cp + l1 + 1, n);
540+
add_next_index_stringl(entries, cp + l1 + 1, n, 1);
523541
}
524-
memcpy(tp + ll , cp + ll + 1, n);
525-
add_next_index_stringl(entries, cp + ll + 1, n, 1);
526-
ll = ll + n + 1;
542+
l1 = l1 + n + 1;
543+
l2 = l2 + n;
527544
}
528-
tp[dlen] = '\0';
545+
tp[l2] = '\0';
529546
cp += dlen;
530547

531-
add_assoc_stringl(*subarray, "txt", tp, (dlen>0)?dlen - 1:0, 0);
548+
add_assoc_stringl(*subarray, "txt", tp, l2, 0);
532549
add_assoc_zval(*subarray, "entries", entries);
533550
}
534551
break;
535552
case DNS_T_SOA:
536553
add_assoc_string(*subarray, "type", "SOA", 1);
537-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2);
554+
n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
538555
if (n < 0) {
539556
return NULL;
540557
}
541558
cp += n;
542559
add_assoc_string(*subarray, "mname", name, 1);
543-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2);
560+
n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
544561
if (n < 0) {
545562
return NULL;
546563
}
547564
cp += n;
548565
add_assoc_string(*subarray, "rname", name, 1);
566+
CHECKCP(5*4);
549567
GETLONG(n, cp);
550568
add_assoc_long(*subarray, "serial", n);
551569
GETLONG(n, cp);
@@ -559,6 +577,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
559577
break;
560578
case DNS_T_AAAA:
561579
tp = (u_char*)name;
580+
CHECKCP(8*2);
562581
for(i=0; i < 8; i++) {
563582
GETSHORT(s, cp);
564583
if (s != 0) {
@@ -593,6 +612,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
593612
case DNS_T_A6:
594613
p = cp;
595614
add_assoc_string(*subarray, "type", "A6", 1);
615+
CHECKCP(1);
596616
n = ((int)cp[0]) & 0xFF;
597617
cp++;
598618
add_assoc_long(*subarray, "masklen", n);
@@ -628,6 +648,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
628648
cp++;
629649
}
630650
for (i = (n + 8) / 16; i < 8; i++) {
651+
CHECKCP(2);
631652
GETSHORT(s, cp);
632653
if (s != 0) {
633654
if (tp > (u_char *)name) {
@@ -657,7 +678,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
657678
tp[0] = '\0';
658679
add_assoc_string(*subarray, "ipv6", name, 1);
659680
if (cp < p + dlen) {
660-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
681+
n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
661682
if (n < 0) {
662683
return NULL;
663684
}
@@ -666,36 +687,51 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
666687
}
667688
break;
668689
case DNS_T_SRV:
690+
CHECKCP(3*2);
669691
add_assoc_string(*subarray, "type", "SRV", 1);
670692
GETSHORT(n, cp);
671693
add_assoc_long(*subarray, "pri", n);
672694
GETSHORT(n, cp);
673695
add_assoc_long(*subarray, "weight", n);
674696
GETSHORT(n, cp);
675697
add_assoc_long(*subarray, "port", n);
676-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
698+
n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
677699
if (n < 0) {
678700
return NULL;
679701
}
680702
cp += n;
681703
add_assoc_string(*subarray, "target", name, 1);
682704
break;
683705
case DNS_T_NAPTR:
706+
CHECKCP(2*2);
684707
add_assoc_string(*subarray, "type", "NAPTR", 1);
685708
GETSHORT(n, cp);
686709
add_assoc_long(*subarray, "order", n);
687710
GETSHORT(n, cp);
688711
add_assoc_long(*subarray, "pref", n);
712+
713+
CHECKCP(1);
689714
n = (cp[0] & 0xFF);
690-
add_assoc_stringl(*subarray, "flags", (char*)++cp, n, 1);
715+
cp++;
716+
CHECKCP(n);
717+
add_assoc_stringl(*subarray, "flags", (char*)cp, n, 1);
691718
cp += n;
719+
720+
CHECKCP(1);
692721
n = (cp[0] & 0xFF);
693-
add_assoc_stringl(*subarray, "services", (char*)++cp, n, 1);
722+
cp++;
723+
CHECKCP(n);
724+
add_assoc_stringl(*subarray, "services", (char*)cp, n, 1);
694725
cp += n;
726+
727+
CHECKCP(1);
695728
n = (cp[0] & 0xFF);
696-
add_assoc_stringl(*subarray, "regex", (char*)++cp, n, 1);
729+
cp++;
730+
CHECKCP(n);
731+
add_assoc_stringl(*subarray, "regex", (char*)cp, n, 1);
697732
cp += n;
698-
n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
733+
734+
n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
699735
if (n < 0) {
700736
return NULL;
701737
}
@@ -888,7 +924,7 @@ PHP_FUNCTION(dns_get_record)
888924
while (an-- && cp && cp < end) {
889925
zval *retval;
890926

891-
cp = php_parserr(cp, &answer, type_to_fetch, store_results, raw, &retval);
927+
cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval);
892928
if (retval != NULL && store_results) {
893929
add_next_index_zval(return_value, retval);
894930
}
@@ -901,7 +937,7 @@ PHP_FUNCTION(dns_get_record)
901937
while (ns-- > 0 && cp && cp < end) {
902938
zval *retval = NULL;
903939

904-
cp = php_parserr(cp, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
940+
cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
905941
if (retval != NULL) {
906942
add_next_index_zval(authns, retval);
907943
}
@@ -913,7 +949,7 @@ PHP_FUNCTION(dns_get_record)
913949
while (ar-- > 0 && cp && cp < end) {
914950
zval *retval = NULL;
915951

916-
cp = php_parserr(cp, &answer, DNS_T_ANY, 1, raw, &retval);
952+
cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval);
917953
if (retval != NULL) {
918954
add_next_index_zval(addtl, retval);
919955
}

0 commit comments

Comments
 (0)