@@ -541,94 +541,130 @@ ZEND_API bool zend_ini_parse_bool(zend_string *str)
541
541
}
542
542
}
543
543
544
- ZEND_API zend_long zend_ini_parse_quantity (zend_string * value , zend_string * * errstr ) /* {{{ */
544
+ static zend_long zend_ini_parse_quantity_internal (zend_string * value , bool signed_result , zend_string * * errstr ) /* {{{ */
545
545
{
546
546
char * digits_end = NULL ;
547
547
char * str = ZSTR_VAL (value );
548
- size_t str_len = ZSTR_LEN (value );
548
+ char * str_end = & str [ZSTR_LEN (value )];
549
+ char * digits = str ;
550
+ bool overflow = false;
551
+ zend_ulong factor ;
549
552
smart_str invalid = {0 };
550
553
smart_str interpreted = {0 };
551
554
smart_str chr = {0 };
552
555
556
+ /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
557
+ * but we need the position of the first non-whitespace later. */
558
+ while (digits < str_end && zend_is_whitespace (* digits )) ++ digits ;
559
+
553
560
/* Ignore trailing whitespace */
554
- while (str_len && zend_is_whitespace (str [ str_len - 1 ])) -- str_len ;
561
+ while (digits < str_end && zend_is_whitespace (* ( str_end - 1 ))) -- str_end ;
555
562
556
- if (! str_len ) {
563
+ if (digits == str_end ) {
557
564
* errstr = NULL ;
558
565
return 0 ;
559
566
}
560
567
561
- /* Perform following multiplications on unsigned to avoid overflow UB.
562
- * For now overflow is silently ignored -- not clear what else can be
563
- * done here, especially as the final result of this function may be
564
- * used in an unsigned context (e.g. "memory_limit=3G", which overflows
565
- * zend_long on 32-bit, but not size_t). */
566
- zend_ulong retval = (zend_ulong ) ZEND_STRTOL (str , & digits_end , 0 );
568
+ zend_ulong retval ;
569
+ errno = 0 ;
570
+
571
+ if (signed_result ) {
572
+ retval = (zend_ulong ) ZEND_STRTOL (digits , & digits_end , 0 );
573
+ } else {
574
+ retval = ZEND_STRTOUL (digits , & digits_end , 0 );
575
+ }
576
+
577
+ if (errno == ERANGE ) {
578
+ overflow = true;
579
+ } else if (!signed_result ) {
580
+ /* ZEND_STRTOUL() does not report a range error when the subject starts
581
+ * with a minus sign, so we check this here. Ignore "-1" as it is
582
+ * commonly used as max value, for instance in memory_limit=-1. */
583
+ if (digits [0 ] == '-' && !(digits_end - digits == 2 && digits_end == str_end && digits [1 ] == '1' )) {
584
+ overflow = true;
585
+ }
586
+ }
587
+
588
+ if (UNEXPECTED (digits_end == digits )) {
589
+ /* No leading digits */
567
590
568
- if (digits_end == str ) {
569
- smart_str_append_escaped (& invalid , str , str_len );
591
+ /* Escape the string to avoid null bytes and to make non-printable chars
592
+ * visible */
593
+ smart_str_append_escaped (& invalid , ZSTR_VAL (value ), ZSTR_LEN (value ));
570
594
smart_str_0 (& invalid );
571
595
572
- * errstr = zend_strpprintf (0 , "Invalid quantity '%s' : no valid leading digits, interpreting as '0' for backwards compatibility" ,
596
+ * errstr = zend_strpprintf (0 , "Invalid quantity \"%s\" : no valid leading digits, interpreting as \"0\" for backwards compatibility" ,
573
597
ZSTR_VAL (invalid .s ));
574
598
575
599
smart_str_free (& invalid );
576
600
return 0 ;
577
601
}
578
602
579
603
/* Allow for whitespace between integer portion and any suffix character */
580
- while (digits_end < & str [ str_len ] && zend_is_whitespace (* digits_end )) ++ digits_end ;
604
+ while (digits_end < str_end && zend_is_whitespace (* digits_end )) ++ digits_end ;
581
605
582
606
/* No exponent suffix. */
583
- if (digits_end == & str [str_len ]) {
584
- * errstr = NULL ;
585
- return retval ;
586
- }
587
-
588
- if (str_len > 0 ) {
589
- switch (str [str_len - 1 ]) {
590
- case 'g' :
591
- case 'G' :
592
- retval *= 1024 ;
593
- ZEND_FALLTHROUGH ;
594
- case 'm' :
595
- case 'M' :
596
- retval *= 1024 ;
597
- ZEND_FALLTHROUGH ;
598
- case 'k' :
599
- case 'K' :
600
- retval *= 1024 ;
601
- break ;
602
- default :
603
- /* Unknown suffix */
604
- smart_str_append_escaped (& invalid , str , str_len );
605
- smart_str_0 (& invalid );
606
- smart_str_append_escaped (& interpreted , str , digits_end - str );
607
- smart_str_0 (& interpreted );
608
- smart_str_append_escaped (& chr , & str [str_len - 1 ], 1 );
609
- smart_str_0 (& chr );
610
-
611
- * errstr = zend_strpprintf (0 , "Invalid quantity '%s': unknown multipler '%s', interpreting as '%s' for backwards compatibility" ,
612
- ZSTR_VAL (invalid .s ), ZSTR_VAL (chr .s ), ZSTR_VAL (interpreted .s ));
613
-
614
- smart_str_free (& invalid );
615
- smart_str_free (& interpreted );
616
- smart_str_free (& chr );
617
-
618
- return retval ;
607
+ if (digits_end == str_end ) {
608
+ goto end ;
609
+ }
610
+
611
+ switch (* (str_end - 1 )) {
612
+ case 'g' :
613
+ case 'G' :
614
+ factor = 1 <<30 ;
615
+ break ;
616
+ case 'm' :
617
+ case 'M' :
618
+ factor = 1 <<20 ;
619
+ break ;
620
+ case 'k' :
621
+ case 'K' :
622
+ factor = 1 <<10 ;
623
+ break ;
624
+ default :
625
+ /* Unknown suffix */
626
+ smart_str_append_escaped (& invalid , ZSTR_VAL (value ), ZSTR_LEN (value ));
627
+ smart_str_0 (& invalid );
628
+ smart_str_append_escaped (& interpreted , str , digits_end - str );
629
+ smart_str_0 (& interpreted );
630
+ smart_str_append_escaped (& chr , str_end - 1 , 1 );
631
+ smart_str_0 (& chr );
632
+
633
+ * errstr = zend_strpprintf (0 , "Invalid quantity \"%s\": unknown multipler \"%s\", interpreting as \"%s\" for backwards compatibility" ,
634
+ ZSTR_VAL (invalid .s ), ZSTR_VAL (chr .s ), ZSTR_VAL (interpreted .s ));
635
+
636
+ smart_str_free (& invalid );
637
+ smart_str_free (& interpreted );
638
+ smart_str_free (& chr );
639
+
640
+ return retval ;
641
+ }
642
+
643
+ if (!overflow ) {
644
+ if (signed_result ) {
645
+ zend_long sretval = (zend_long )retval ;
646
+ if (sretval > 0 ) {
647
+ overflow = (zend_long )retval > ZEND_LONG_MAX / (zend_long )factor ;
648
+ } else {
649
+ overflow = (zend_long )retval < ZEND_LONG_MIN / (zend_long )factor ;
650
+ }
651
+ } else {
652
+ overflow = retval > ZEND_ULONG_MAX / factor ;
619
653
}
620
654
}
621
655
622
- if (digits_end < & str [str_len - 1 ]) {
656
+ retval *= factor ;
657
+
658
+ if (UNEXPECTED (digits_end != str_end - 1 )) {
623
659
/* More than one character in suffix */
624
- smart_str_append_escaped (& invalid , str , str_len );
660
+ smart_str_append_escaped (& invalid , ZSTR_VAL ( value ), ZSTR_LEN ( value ) );
625
661
smart_str_0 (& invalid );
626
662
smart_str_append_escaped (& interpreted , str , digits_end - str );
627
663
smart_str_0 (& interpreted );
628
- smart_str_append_escaped (& chr , & str [ str_len - 1 ] , 1 );
664
+ smart_str_append_escaped (& chr , str_end - 1 , 1 );
629
665
smart_str_0 (& chr );
630
666
631
- * errstr = zend_strpprintf (0 , "Invalid quantity '%s' , interpreting as ' %s%s' for backwards compatibility" ,
667
+ * errstr = zend_strpprintf (0 , "Invalid quantity \"%s\" , interpreting as \" %s%s\" for backwards compatibility" ,
632
668
ZSTR_VAL (invalid .s ), ZSTR_VAL (interpreted .s ), ZSTR_VAL (chr .s ));
633
669
634
670
smart_str_free (& invalid );
@@ -638,11 +674,41 @@ ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **err
638
674
return (zend_long ) retval ;
639
675
}
640
676
677
+ end :
678
+ if (UNEXPECTED (overflow )) {
679
+ smart_str_append_escaped (& invalid , ZSTR_VAL (value ), ZSTR_LEN (value ));
680
+ smart_str_0 (& invalid );
681
+
682
+ /* Not specifying the resulting value here because the caller may make
683
+ * additional conversions. Not specifying the allowed range
684
+ * because the caller may do narrower range checks. */
685
+ * errstr = zend_strpprintf (0 , "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility" ,
686
+ ZSTR_VAL (invalid .s ));
687
+
688
+ smart_str_free (& invalid );
689
+ smart_str_free (& interpreted );
690
+ smart_str_free (& chr );
691
+
692
+ return (zend_long ) retval ;
693
+ }
694
+
641
695
* errstr = NULL ;
642
696
return (zend_long ) retval ;
643
697
}
644
698
/* }}} */
645
699
700
+ ZEND_API zend_long zend_ini_parse_quantity (zend_string * value , zend_string * * errstr ) /* {{{ */
701
+ {
702
+ return zend_ini_parse_quantity_internal (value , true, errstr );
703
+ }
704
+ /* }}} */
705
+
706
+ zend_ulong zend_ini_parse_uquantity (zend_string * value , zend_string * * errstr ) /* {{{ */
707
+ {
708
+ return (zend_ulong ) zend_ini_parse_quantity_internal (value , false, errstr );
709
+ }
710
+ /* }}} */
711
+
646
712
ZEND_INI_DISP (zend_ini_boolean_displayer_cb ) /* {{{ */
647
713
{
648
714
int value ;
0 commit comments