]> granicus.if.org Git - php/commitdiff
Make bin2hex() and hex2bin() timing safe
authorBob Weinand <bobwei9@hotmail.com>
Tue, 4 Aug 2015 20:07:13 +0000 (22:07 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Tue, 4 Aug 2015 20:07:13 +0000 (22:07 +0200)
ext/standard/string.c

index a39105e20bd4e414963d13bcf0bb643d673f533d..9c1031effb52bbe2648f6cc70de8ee004e43bf42 100644 (file)
@@ -117,8 +117,16 @@ void register_string_constants(INIT_FUNC_ARGS)
 
 int php_tag_find(char *tag, size_t len, const char *set);
 
+#ifdef PHP_WIN32
+# define SET_ALIGNED(alignment, decl) __declspec(align(alignment)) decl
+#elif HAVE_ATTRIBUTE_ALIGNED
+# define SET_ALIGNED(alignment, decl) decl __attribute__ ((__aligned__ (alignment)))
+#else
+# define SET_ALIGNED(alignment, decl) decl
+#endif
+
 /* this is read-only, so it's ok */
-static char hexconvtab[] = "0123456789abcdef";
+SET_ALIGNED(16, static char hexconvtab[]) = "0123456789abcdef";
 
 /* localeconv mutex */
 #ifdef ZTS
@@ -155,25 +163,22 @@ static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
 
        for (i = j = 0; i < target_length; i++) {
                unsigned char c = old[j++];
+               unsigned char l = c & ~0x20;
+               int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
                unsigned char d;
 
-               if (c >= '0' && c <= '9') {
-                       d = (c - '0') << 4;
-               } else if (c >= 'a' && c <= 'f') {
-                       d = (c - 'a' + 10) << 4;
-               } else if (c >= 'A' && c <= 'F') {
-                       d = (c - 'A' + 10) << 4;
+               /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */ 
+               if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
+                       d = (l - 0x10 - 0x27 * is_letter) << 4;
                } else {
                        zend_string_free(str);
                        return NULL;
                }
                c = old[j++];
-               if (c >= '0' && c <= '9') {
-                       d |= c - '0';
-               } else if (c >= 'a' && c <= 'f') {
-                       d |= c - 'a' + 10;
-               } else if (c >= 'A' && c <= 'F') {
-                       d |= c - 'A' + 10;
+               l = c & ~0x20;
+               is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
+               if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
+                       d |= l - 0x10 - 0x27 * is_letter;
                } else {
                        zend_string_free(str);
                        return NULL;