locale.c: Use my_strlcat() blead
authorKarl Williamson <khw@cpan.org>
Tue, 23 Aug 2016 19:32:05 +0000 (13:32 -0600)
committerKarl Williamson <khw@cpan.org>
Thu, 25 Aug 2016 17:31:26 +0000 (11:31 -0600)
strcat() is safe in this context, but some compilers were optimizing
this to strcpy() causing a porting test to fail that looks for unsafe
code.  Rather than fighting this, just use my_strlcat().  The code is
rarely executed.  But at the same time, I used the return value of that
function to know where to start the next cat in the next loop iteration
without having to have the cat code search for the trailing NUL.

locale.c
t/porting/libperl.t

index 9f64d80..779324e 100644 (file)
--- a/locale.c
+++ b/locale.c
@@ -1462,15 +1462,13 @@ Perl__mem_collxfrm(pTHX_ const char *input_string,
      * This will give as good as possible results on strings that don't
      * otherwise contain that character, but otherwise there may be
      * less-than-perfect results with that character and NUL.  This is
-     * unavoidable unless we replace strxfrm with our own implementation.
-     *
-     * This is one of the few places in the perl core, where we can use
-     * standard functions like strlen() and strcat().  It's because we're
-     * looking for NULs. */
+     * unavoidable unless we replace strxfrm with our own implementation. */
     if (s_strlen < len) {
         char * e = s + len;
         char * sans_nuls;
         STRLEN cur_min_char_len;
+        STRLEN sans_nuls_len;
+        STRLEN sans_nuls_pos;
         int try_non_controls;
 
         /* If we don't know what control character sorts lowest for this
@@ -1576,16 +1574,22 @@ Perl__mem_collxfrm(pTHX_ const char *input_string,
          * character in it is NUL.  Multiply that by the length of each
          * replacement, and allow for a trailing NUL */
         cur_min_char_len = strlen(PL_strxfrm_min_char);
-        Newx(sans_nuls, (len * cur_min_char_len) + 1, char);
+        sans_nuls_len = (len * cur_min_char_len) + 1;
+        Newx(sans_nuls, sans_nuls_len, char);
         *sans_nuls = '\0';
+        sans_nuls_pos = 0;
 
         /* Replace each NUL with the lowest collating control.  Loop until have
          * exhausted all the NULs */
         while (s + s_strlen < e) {
-            strcat(sans_nuls, s);
+            sans_nuls_pos = my_strlcat(sans_nuls + sans_nuls_pos,
+                                       s,
+                                       sans_nuls_len);
 
             /* Do the actual replacement */
-            strcat(sans_nuls, PL_strxfrm_min_char);
+            sans_nuls_pos = my_strlcat(sans_nuls + sans_nuls_pos,
+                                       PL_strxfrm_min_char,
+                                       sans_nuls_len);
 
             /* Move past the input NUL */
             s += s_strlen + 1;
@@ -1593,7 +1597,7 @@ Perl__mem_collxfrm(pTHX_ const char *input_string,
         }
 
         /* And add anything that trails the final NUL */
-        strcat(sans_nuls, s);
+        my_strlcat(sans_nuls + sans_nuls_pos, s, sans_nuls_len);
 
         /* Switch so below we transform this modified string */
         s = sans_nuls;
index 21e7edb..4a3e568 100644 (file)
@@ -540,13 +540,6 @@ for my $symbol (sort keys %unexpected) {
       SKIP: {
         skip("uses sprintf for Gconvert in sv.o");
       }
-    }
-    elsif (   $symbol eq 'strcat'
-           && @o == 1 && $o[0] eq 'locale.o')
-    {
-      SKIP: {
-        skip("locale.o legitimately uses strcat");
-      }
     } else {
         is(@o, 0, "uses no $symbol (@o)");
     }