1  /* Alignment/padding coverage test for string comparison.
       2     Copyright (C) 2016-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* This performs test comparisons with various (mis)alignments and
      20     characters in the padding.  It is partly a regression test for bug
      21     20327.  */
      22  
      23  #include <limits.h>
      24  #include <malloc.h>
      25  #include <stdbool.h>
      26  #include <stdint.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  #include <libc-diag.h>
      30  
      31  static int
      32  signum (int val)
      33  {
      34    if (val < 0)
      35      return -1;
      36    if (val > 0)
      37      return 1;
      38    else
      39      return 0;
      40  }
      41  
      42  static size_t
      43  max_size_t (size_t left, size_t right)
      44  {
      45    if (left > right)
      46      return left;
      47    else
      48      return right;
      49  }
      50  
      51  /* Wrappers for strncmp and strncasecmp which determine the maximum
      52     string length in some, either based on the input string length, or
      53     using fixed constants.  */
      54  
      55  static int
      56  strncmp_no_terminator (const char *left, const char *right)
      57  {
      58    size_t left_len = strlen (left);
      59    size_t right_len = strlen (right);
      60    return strncmp (left, right, max_size_t (left_len, right_len));
      61  }
      62  
      63  static int
      64  strncasecmp_no_terminator (const char *left, const char *right)
      65  {
      66    size_t left_len = strlen (left);
      67    size_t right_len = strlen (right);
      68    return strncasecmp (left, right, max_size_t (left_len, right_len));
      69  }
      70  
      71  static int
      72  strncmp_terminator (const char *left, const char *right)
      73  {
      74    size_t left_len = strlen (left);
      75    size_t right_len = strlen (right);
      76    return strncmp (left, right, max_size_t (left_len, right_len));
      77  }
      78  
      79  static int
      80  strncasecmp_terminator (const char *left, const char *right)
      81  {
      82    size_t left_len = strlen (left);
      83    size_t right_len = strlen (right);
      84    return strncasecmp (left, right, max_size_t (left_len, right_len));
      85  }
      86  
      87  static int
      88  strncmp_64 (const char *left, const char *right)
      89  {
      90    return strncmp (left, right, 64);
      91  }
      92  
      93  static int
      94  strncasecmp_64 (const char *left, const char *right)
      95  {
      96    return strncasecmp (left, right, 64);
      97  }
      98  
      99  static int
     100  strncmp_max (const char *left, const char *right)
     101  {
     102    DIAG_PUSH_NEEDS_COMMENT;
     103  #if __GNUC_PREREQ (7, 0)
     104    /* GCC 9 warns about the size passed to strncmp being larger than
     105       PTRDIFF_MAX; the use of SIZE_MAX is deliberate here.  */
     106    DIAG_IGNORE_NEEDS_COMMENT (9, "-Wstringop-overflow=");
     107  #endif
     108  #if __GNUC_PREREQ (11, 0)
     109    /* Likewise GCC 11, with a different warning option.  */
     110    DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overread");
     111  #endif
     112    return strncmp (left, right, SIZE_MAX);
     113    DIAG_POP_NEEDS_COMMENT;
     114  }
     115  
     116  static int
     117  strncasecmp_max (const char *left, const char *right)
     118  {
     119    DIAG_PUSH_NEEDS_COMMENT;
     120  #if __GNUC_PREREQ (7, 0)
     121    /* GCC 9 warns about the size passed to strncasecmp being larger
     122       than PTRDIFF_MAX; the use of SIZE_MAX is deliberate here.  */
     123    DIAG_IGNORE_NEEDS_COMMENT (9, "-Wstringop-overflow=");
     124  #endif
     125  #if __GNUC_PREREQ (11, 0)
     126    /* Likewise GCC 11, with a different warning option.  */
     127    DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overread");
     128  #endif
     129    return strncasecmp (left, right, SIZE_MAX);
     130    DIAG_POP_NEEDS_COMMENT;
     131  }
     132  
     133  int
     134  do_test (void)
     135  {
     136    enum {
     137      max_align = 64,
     138      max_string_length = 33
     139    };
     140    size_t blob_size = max_align + max_string_length + 1;
     141    char *left = memalign (max_align, blob_size);
     142    char *right = memalign (max_align, blob_size);
     143    if (left == NULL || right == NULL)
     144      {
     145        printf ("error: out of memory\n");
     146        return 1;
     147      }
     148  
     149    const struct
     150    {
     151      const char *name;
     152      int (*implementation) (const char *, const char *);
     153    } functions[] =
     154        {
     155          { "strcmp", strcmp },
     156          { "strcasecmp", strcasecmp },
     157          { "strncmp (without NUL)", strncmp_no_terminator},
     158          { "strncasecmp (without NUL)", strncasecmp_no_terminator},
     159          { "strncmp (with NUL)", strncmp_terminator},
     160          { "strncasecmp (with NUL)", strncasecmp_terminator},
     161          { "strncmp (length 64)", strncmp_64},
     162          { "strncasecmp (length 64)", strncasecmp_64},
     163          { "strncmp (length SIZE_MAX)", strncmp_max},
     164          { "strncasecmp (length SIZE_MAX)", strncasecmp_max},
     165          { NULL, NULL }
     166        };
     167    const char *const strings[] =
     168      {
     169        "",
     170        "0",
     171        "01",
     172        "01234567",
     173        "0123456789abcde",
     174        "0123456789abcdef",
     175        "0123456789abcdefg",
     176        "1",
     177        "10",
     178        "123456789abcdef",
     179        "123456789abcdefg",
     180        "23456789abcdef",
     181        "23456789abcdefg",
     182        "abcdefghijklmnopqrstuvwxyzABCDEF",
     183        NULL
     184      };
     185    const unsigned char pads[] =
     186      { 0, 1, 32, 64, 128, '0', '1', 'e', 'f', 'g', 127, 192, 255 };
     187  
     188    bool errors = false;
     189    for (int left_idx = 0; strings[left_idx] != NULL; ++left_idx)
     190      for (int left_align = 0; left_align < max_align; ++left_align)
     191        for (unsigned pad_left = 0; pad_left < sizeof (pads); ++pad_left)
     192          {
     193            memset (left, pads[pad_left], blob_size);
     194            strcpy (left + left_align, strings[left_idx]);
     195  
     196            for (int right_idx = 0; strings[right_idx] != NULL; ++right_idx)
     197              for (unsigned pad_right = 0; pad_right < sizeof (pads);
     198                   ++pad_right)
     199                for (int right_align = 0; right_align < max_align;
     200                     ++right_align)
     201                  {
     202                    memset (right, pads[pad_right], blob_size);
     203                    strcpy (right + right_align, strings[right_idx]);
     204  
     205                    for (int func = 0; functions[func].name != NULL; ++func)
     206                      {
     207                        int expected = left_idx - right_idx;
     208                        int actual = functions[func].implementation
     209                          (left + left_align, right + right_align);
     210                        if (signum (actual) != signum (expected))
     211                          {
     212                            printf ("error: mismatch for %s: %d\n"
     213                                    "  left:  \"%s\"\n"
     214                                    "  right: \"%s\"\n"
     215                                    "  pad_left = %u, pad_right = %u,\n"
     216                                    "  left_align = %d, right_align = %d\n",
     217                                    functions[func].name, actual,
     218                                    strings[left_idx], strings[right_idx],
     219                                    pad_left, pad_right,
     220                                    left_align, right_align);
     221                            errors = true;
     222                          }
     223                      }
     224                  }
     225          }
     226    free (right);
     227    free (left);
     228    return errors;
     229  }
     230  
     231  /* The nested loops need a long time to complete on slower
     232     machines.  */
     233  #define TIMEOUT 600
     234  
     235  #include <support/test-driver.c>