Changeset 0600976 in mainline


Ignore:
Timestamp:
2025-04-14T11:23:38Z (6 days ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Branches:
master
Children:
5d2bdaa
Parents:
11782da
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2025-04-14 10:57:38)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2025-04-14 11:23:38)
Message:

Reject invalid non-shortest UTF-8 forms and fix some other issues in str

Files:
3 edited

Legend:

Unmodified
Added
Removed
  • common/stdc/uchar.c

    r11782da r0600976  
    8484}
    8585
     86static bool _is_non_shortest(unsigned short cont, uint8_t b)
     87{
     88        return (cont == 0b1111110000000000 && !(b & 0b00100000)) ||
     89            (cont == 0b1111111111110000 && !(b & 0b00110000));
     90}
     91
    8692size_t mbrtoc32(char32_t *c, const char *s, size_t n, mbstate_t *mb)
    8793{
     
    139145
    140146                if (_is_2_byte(b)) {
     147                        /* Reject non-shortest form. */
     148                        if (!(b & 0b00011110)) {
     149                                _set_ilseq();
     150                                return UCHAR_ILSEQ;
     151                        }
     152
    141153                        /* 2 byte encoding               110xxxxx */
    142154                        mb->continuation = b ^ 0b0000000011000000;
     
    152164        }
    153165
    154         while (i < n) {
     166        for (; i < n; i++) {
    155167                /* Read continuation bytes. */
    156 
    157                 if (!_is_continuation(s[i])) {
     168                uint8_t b = s[i];
     169
     170                if (!_is_continuation(b) || _is_non_shortest(mb->continuation, b)) {
    158171                        _set_ilseq();
    159172                        return UCHAR_ILSEQ;
     
    162175                /* Top bit becomes zero just before the last byte is shifted in. */
    163176                if (!(mb->continuation & 0x8000)) {
    164                         *c = ((char32_t) mb->continuation) << 6 | (s[i++] & 0x3f);
     177                        *c = ((char32_t) mb->continuation) << 6 | (b & 0x3f);
    165178                        mb->continuation = 0;
    166                         return i;
    167                 }
    168 
    169                 mb->continuation = mb->continuation << 6 | (s[i++] & 0x3f);
     179                        return ++i;
     180                }
     181
     182                mb->continuation = mb->continuation << 6 | (b & 0x3f);
    170183        }
    171184
  • common/str.c

    r11782da r0600976  
    210210char32_t str_decode(const char *str, size_t *offset, size_t size)
    211211{
    212         if (*offset + 1 > size)
     212        if (*offset >= size)
    213213                return 0;
    214214
     
    235235
    236236        /* Decode continuation bytes */
    237         while (cbytes > 0) {
     237        for (int i = 0; i < cbytes; i++) {
    238238                uint8_t b = (uint8_t) str[*offset];
    239239
     
    245245                /* Shift data bits to ch */
    246246                ch = (ch << CONT_BITS) | (char32_t) (b & LO_MASK_8(CONT_BITS));
    247                 cbytes--;
    248         }
     247        }
     248
     249        /*
     250         * Reject non-shortest form encodings.
     251         * See https://www.unicode.org/versions/corrigendum1.html
     252         */
     253        if (cbytes != _char_continuation_bytes(ch))
     254                return U_SPECIAL;
    249255
    250256        return ch;
     
    350356
    351357/* Convert in place any bytes that don't form a valid character into U_SPECIAL. */
    352 static void _repair_string(char *str, size_t n)
    353 {
    354         for (; *str && n > 0; str++, n--) {
    355                 int cont = _continuation_bytes(*str);
    356                 if (cont == 0)
     358static void _sanitize_string(char *str, size_t n)
     359{
     360        uint8_t *b = (uint8_t *) str;
     361
     362        for (; *b && n > 0; b++, n--) {
     363                int cont = _continuation_bytes(b[0]);
     364                if (__builtin_expect(cont, 0) == 0)
    357365                        continue;
    358366
    359367                if (cont < 0 || n <= (size_t) cont) {
    360                         *str = U_SPECIAL;
     368                        b[0] = U_SPECIAL;
    361369                        continue;
    362370                }
    363371
     372                /* Check continuation bytes. */
    364373                for (int i = 1; i <= cont; i++) {
    365                         if (!_is_continuation_byte(str[i])) {
    366                                 *str = U_SPECIAL;
     374                        if (!_is_continuation_byte(b[i])) {
     375                                b[0] = U_SPECIAL;
    367376                                continue;
    368377                        }
     378                }
     379
     380                /*
     381                 * Check for non-shortest form encoding.
     382                 * See https://www.unicode.org/versions/corrigendum1.html
     383                 */
     384
     385                switch (cont) {
     386                case 1:
     387                        /* 0b110!!!!x 0b10xxxxxx */
     388                        if (!(b[0] & 0b00011110))
     389                                b[0] = U_SPECIAL;
     390
     391                        continue;
     392                case 2:
     393                        /* 0b1110!!!! 0b10!xxxxx 0b10xxxxxx */
     394                        if (!(b[0] & 0b00001111) && !(b[1] & 0b00100000))
     395                                b[0] = U_SPECIAL;
     396
     397                        continue;
     398                case 3:
     399                        /* 0b11110!!! 0b10!!xxxx 0b10xxxxxx 0b10xxxxxx */
     400                        if (!(b[0] & 0b00000111) && !(b[1] & 0b00110000))
     401                                b[0] = U_SPECIAL;
     402
     403                        continue;
    369404                }
    370405        }
     
    886921static void _str_cpyn(char *dest, size_t size, const char *src)
    887922{
     923        assert(dest && src && size);
     924
     925        if (!dest || !src || !size)
     926                return;
     927
     928        if (size == STR_NO_LIMIT)
     929                return _str_cpy(dest, src);
     930
    888931        char *dest_top = dest + size - 1;
     932        assert(size == 1 || dest < dest_top);
    889933
    890934        while (*src && dest < dest_top)
     
    912956        assert(src != NULL);
    913957        assert(dest != NULL);
     958        assert(size == STR_NO_LIMIT || dest + size > dest);
    914959
    915960        /* Copy data. */
     
    917962
    918963        /* In-place translate invalid bytes to U_SPECIAL. */
    919         _repair_string(dest, size);
     964        _sanitize_string(dest, size);
    920965}
    921966
     
    946991
    947992        /* In-place translate invalid bytes to U_SPECIAL. */
    948         _repair_string(dest, size);
     993        _sanitize_string(dest, size);
    949994}
    950995
     
    9651010        assert(dest != NULL);
    9661011        assert(size > 0);
     1012        assert(size == STR_NO_LIMIT || dest + size > dest);
    9671013
    9681014        size_t dstr_size = _str_nsize(dest, size);
    969         _str_cpyn(dest + dstr_size, size - dstr_size, src);
    970         _repair_string(dest + dstr_size, size - dstr_size);
     1015        if (dstr_size < size) {
     1016                _str_cpyn(dest + dstr_size, size - dstr_size, src);
     1017                _sanitize_string(dest + dstr_size, size - dstr_size);
     1018        }
    9711019}
    9721020
     
    15451593                return NULL;
    15461594
    1547         _str_cpy(dest, src);
    1548         _repair_string(dest, size);
     1595        memcpy(dest, src, size);
     1596        _sanitize_string(dest, size);
    15491597        return dest;
    15501598}
     
    15721620char *str_ndup(const char *src, size_t n)
    15731621{
    1574         size_t size = _str_nsize(src, n) + 1;
    1575 
    1576         char *dest = malloc(size);
     1622        size_t size = _str_nsize(src, n);
     1623
     1624        char *dest = malloc(size + 1);
    15771625        if (!dest)
    15781626                return NULL;
    15791627
    1580         _str_cpyn(dest, size, src);
    1581         _repair_string(dest, size);
     1628        memcpy(dest, src, size);
     1629        _sanitize_string(dest, size);
     1630        dest[size] = 0;
    15821631        return dest;
    15831632}
  • uspace/lib/c/test/str.c

    r11782da r0600976  
    2727 */
    2828
     29#include "pcut/asserts.h"
    2930#include <stdio.h>
    3031#include <str.h>
     
    115116}
    116117
     118PCUT_TEST(str_non_shortest)
     119{
     120        /* Overlong zero. */
     121        const char overlong1[] = { 0b11000000, 0b10000000, 0 };
     122        const char overlong2[] = { 0b11100000, 0b10000000, 0 };
     123        const char overlong3[] = { 0b11110000, 0b10000000, 0 };
     124
     125        const char overlong4[] = { 0b11000001, 0b10111111, 0 };
     126        const char overlong5[] = { 0b11100000, 0b10011111, 0b10111111, 0 };
     127        const char overlong6[] = { 0b11110000, 0b10001111, 0b10111111, 0b10111111, 0 };
     128
     129        size_t offset = 0;
     130        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong1, &offset, sizeof(overlong1)));
     131        offset = 0;
     132        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong2, &offset, sizeof(overlong2)));
     133        offset = 0;
     134        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong3, &offset, sizeof(overlong3)));
     135        offset = 0;
     136        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong4, &offset, sizeof(overlong4)));
     137        offset = 0;
     138        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong5, &offset, sizeof(overlong5)));
     139        offset = 0;
     140        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong6, &offset, sizeof(overlong6)));
     141
     142        char sanitized[sizeof(overlong6)];
     143        str_cpy(sanitized, STR_NO_LIMIT, overlong1);
     144        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, sanitized[0]);
     145        str_cpy(sanitized, STR_NO_LIMIT, overlong2);
     146        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, sanitized[0]);
     147        str_cpy(sanitized, STR_NO_LIMIT, overlong3);
     148        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, sanitized[0]);
     149        str_cpy(sanitized, STR_NO_LIMIT, overlong4);
     150        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, sanitized[0]);
     151        str_cpy(sanitized, STR_NO_LIMIT, overlong5);
     152        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, sanitized[0]);
     153        str_cpy(sanitized, STR_NO_LIMIT, overlong6);
     154        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, sanitized[0]);
     155}
     156
    117157PCUT_EXPORT(str);
Note: See TracChangeset for help on using the changeset viewer.