@@ -529,6 +529,40 @@ ssl_error_fetch_lib_and_reason(_sslmodulestate *state, py_ssl_errcode errcode,
529
529
* reason = Py_XNewRef (val );
530
530
}
531
531
532
+ static inline size_t
533
+ ssl_error_filename_width (const char * Py_UNUSED (filename ))
534
+ {
535
+ /*
536
+ * Sometimes, __FILE__ is an absolute path, so we hardcode "_ssl.c".
537
+ * In the future, we might want to use the "filename" parameter but
538
+ * for now (and for backward compatibility), we ignore it.
539
+ */
540
+ return 6 /* strlen("_ssl.c") */ ;
541
+ }
542
+
543
+ static inline size_t
544
+ ssl_error_lineno_width (int lineno )
545
+ {
546
+ if (lineno < 0 ) {
547
+ return 1 + ssl_error_lineno_width (- lineno );
548
+ }
549
+ #define FAST_PATH (E , N ) \
550
+ do { \
551
+ assert((size_t)(1e ## E) == N); \
552
+ if (lineno < (N)) { \
553
+ return (E); \
554
+ } \
555
+ } while (0)
556
+ FAST_PATH (2 , 10 );
557
+ FAST_PATH (3 , 100 );
558
+ FAST_PATH (4 , 1000 );
559
+ FAST_PATH (5 , 10000 );
560
+ FAST_PATH (6 , 100000 );
561
+ FAST_PATH (7 , 1000000 );
562
+ #undef FAST_PATH
563
+ return (size_t )ceil (log10 (lineno ));
564
+ }
565
+
532
566
/*
533
567
* Construct a Unicode object containing the formatted SSL error message.
534
568
*
@@ -537,7 +571,7 @@ ssl_error_fetch_lib_and_reason(_sslmodulestate *state, py_ssl_errcode errcode,
537
571
static PyObject *
538
572
format_ssl_error_message (PyObject * lib , PyObject * reason , PyObject * verify ,
539
573
const char * errstr ,
540
- const char * Py_UNUSED ( filename ) , int lineno )
574
+ const char * filename , int lineno )
541
575
{
542
576
assert (errstr != NULL );
543
577
@@ -551,71 +585,71 @@ format_ssl_error_message(PyObject *lib, PyObject *reason, PyObject *verify,
551
585
CHECK_OBJECT (verify );
552
586
#undef CHECK_OBJECT
553
587
554
- #define OPTIONAL_UTF8 (x ) ((x) == NULL ? NULL : PyUnicode_AsUTF8((x)))
555
- const char * libstr = OPTIONAL_UTF8 (lib );
556
- const char * reastr = OPTIONAL_UTF8 (reason );
557
- const char * verstr = OPTIONAL_UTF8 (verify );
558
- #undef OPTIONAL_UTF8
559
-
560
- const size_t errstr_len = strlen (errstr );
561
- const size_t libstr_len = libstr == NULL ? 0 : strlen (libstr );
562
- const size_t reastr_len = reastr == NULL ? 0 : strlen (reastr );
563
- const size_t verstr_len = verstr == NULL ? 0 : strlen (verstr );
564
- /*
565
- * Sometimes, __FILE__ is an absolute path, so we hardcode "_ssl.c".
566
- * In the future, we might want to use the "filename" parameter but
567
- * for now (and for backward compatibility), we ignore it.
568
- */
569
- const size_t filename_len = 6 ; /* strlen("_ssl.c") */
570
- /*
571
- * Crude upper bound on the number of characters taken by the line number.
572
- * We expect -1 <= lineno < 1e8. More lines are unlikely to happen.
573
- */
574
- assert (lineno >= -1 );
575
- assert (lineno < 1e8 );
576
- const size_t lineno_len = 8 ;
577
- const size_t base_alloc = (
578
- libstr_len + reastr_len + verstr_len
579
- + errstr_len + filename_len + lineno_len
580
- );
588
+ const size_t filename_len = ssl_error_filename_width (filename );
589
+ const size_t lineno_len = ssl_error_lineno_width (lineno );
590
+ /* exact length of "(_ssl.c:LINENO)" */
591
+ const size_t suffix_len = 1 + filename_len + 1 + lineno_len + 1 ;
581
592
582
593
int rc ;
583
- char * buf ;
584
-
594
+ PyObject * res = NULL ;
595
+ #define CSTRBUF (x ) ((const char *)PyUnicode_DATA((x)))
596
+ #define CHARBUF (x ) ((char *)PyUnicode_DATA((x)))
585
597
if (lib && reason && verify ) {
586
598
/* [LIB: REASON] ERROR: VERIFY (_ssl.c:LINENO) */
587
- const size_t alloc = base_alloc + (4 + 3 + 4 );
588
- /* PyMem_Malloc() is optimized for small buffers */
589
- buf = PyMem_New (char , alloc );
590
- rc = PyOS_snprintf (buf , alloc , "[%s: %s] %s: %s (_ssl.c:%d)" ,
591
- libstr , reastr , errstr , verstr , lineno );
599
+ const char * lib_cstr = CSTRBUF (lib );
600
+ const char * reason_cstr = CSTRBUF (reason );
601
+ const char * verify_cstr = CSTRBUF (verify );
602
+ const size_t ressize = /* excludes final null byte */ (
603
+ 1 + strlen (lib_cstr ) + 2 + strlen (reason_cstr ) + 1
604
+ + 1 + strlen (errstr ) + 2 + strlen (verify_cstr )
605
+ + 1 + suffix_len
606
+ );
607
+ res = PyUnicode_New (ressize , 127 );
608
+ rc = snprintf (CHARBUF (res ), ressize + 1 , "[%s: %s] %s: %s (_ssl.c:%d)" ,
609
+ lib_cstr , reason_cstr , errstr , verify_cstr , lineno );
592
610
}
593
611
else if (lib && reason ) {
594
612
/* [LIB: REASON] ERROR (_ssl.c:LINENO) */
595
- const size_t alloc = base_alloc + (3 + 2 + 4 );
596
- buf = PyMem_New (char , alloc );
597
- rc = PyOS_snprintf (buf , alloc , "[%s: %s] %s (_ssl.c:%d)" ,
598
- libstr , reastr , errstr , lineno );
613
+ const char * lib_cstr = CSTRBUF (lib );
614
+ const char * reason_cstr = CSTRBUF (reason );
615
+ const size_t ressize = /* excludes final null byte */ (
616
+ 1 + strlen (lib_cstr ) + 2 + strlen (reason_cstr ) + 1
617
+ + 1 + strlen (errstr )
618
+ + 1 + suffix_len
619
+ );
620
+ res = PyUnicode_New (ressize , 127 );
621
+ rc = snprintf (CHARBUF (res ), ressize + 1 , "[%s: %s] %s (_ssl.c:%d)" ,
622
+ lib_cstr , reason_cstr , errstr , lineno );
599
623
}
600
624
else if (lib ) {
601
625
/* [LIB] ERROR (_ssl.c:LINENO) */
602
- const size_t alloc = base_alloc + (2 + 1 + 4 );
603
- buf = PyMem_New (char , alloc );
604
- rc = PyOS_snprintf (buf , alloc , "[%s] %s (_ssl.c:%d)" ,
605
- libstr , errstr , lineno );
626
+ const char * lib_cstr = CSTRBUF (lib );
627
+ const size_t ressize = /* excludes final null byte */ (
628
+ 1 + strlen (lib_cstr ) + 1
629
+ + 1 + strlen (errstr )
630
+ + 1 + suffix_len
631
+ );
632
+ res = PyUnicode_New (ressize , 127 );
633
+ rc = snprintf (CHARBUF (res ), ressize + 1 , "[%s] %s (_ssl.c:%d)" ,
634
+ lib_cstr , errstr , lineno );
606
635
}
607
636
else {
608
637
/* ERROR (_ssl.c:LINENO) */
609
- const size_t alloc = base_alloc + (1 + 1 + 2 );
610
- buf = PyMem_New (char , alloc );
611
- rc = PyOS_snprintf (buf , alloc , "%s (_ssl.c:%d)" ,
612
- errstr , lineno );
638
+ const size_t ressize = /* excludes final null byte */ (
639
+ strlen (errstr )
640
+ + 1 + suffix_len
641
+ );
642
+ res = PyUnicode_New (ressize , 127 );
643
+ rc = snprintf (CHARBUF (res ), ressize + 1 , "%s (_ssl.c:%d)" ,
644
+ errstr , lineno );
645
+ }
646
+ #undef CHARBUF
647
+ #undef CSTRBUF
648
+ if (rc < 0 ) {
649
+ Py_XDECREF (res );
650
+ /* fallback to slow path if snprintf() failed */
651
+ return PyUnicode_FromFormat ("%s (_ssl.c:%d)" , errstr , lineno );
613
652
}
614
-
615
- PyObject * res = rc < 0 /* fallback to slow path if snprintf() failed */
616
- ? PyUnicode_FromFormat ("%s (_ssl.c:%d)" , errstr , lineno )
617
- : PyUnicode_FromString (buf ) /* uses the ASCII fast path */ ;
618
- PyMem_Free (buf );
619
653
return res ;
620
654
}
621
655
@@ -628,7 +662,7 @@ format_ssl_error_message(PyObject *lib, PyObject *reason, PyObject *verify,
628
662
* ssl_errno The SSL error number to pass to the exception constructor.
629
663
* lib The ASCII-encoded library obtained from a packed error code.
630
664
* reason The ASCII-encoded reason obtained from a packed error code.
631
- * errstr The error message to use.
665
+ * errstr The non-NULL error message to use.
632
666
*
633
667
* A non-NULL library or reason is stored in the final exception object.
634
668
*/
@@ -778,10 +812,12 @@ fill_and_set_sslerror(_sslmodulestate *state,
778
812
if (ssl_use_verbose_error (state , exc_type , ssl_errno , errcode )) {
779
813
ssl_error_fetch_lib_and_reason (state , errcode , & lib , & reason );
780
814
}
815
+ if (errstr == NULL && errcode ) {
816
+ errstr = ERR_reason_error_string (errcode );
817
+ }
781
818
if (errstr == NULL ) {
782
- errstr = errcode
783
- ? ERR_reason_error_string (errcode )
784
- : "unknown error" ;
819
+ // ERR_reason_error_string() may return NULL
820
+ errstr = "unknown error" ;
785
821
}
786
822
PyObject * exc ;
787
823
if (sslsock != NULL && exc_type == state -> PySSLCertVerificationErrorObject ) {
0 commit comments