]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
curlx: curlx_strcopy() instead of strcpy()
authorDaniel Stenberg <daniel@haxx.se>
Sun, 21 Dec 2025 22:40:24 +0000 (23:40 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 22 Dec 2025 22:01:05 +0000 (23:01 +0100)
This function REQUIRES the size of the target buffer as well as the
length of the source string. Meant to make it harder to do a bad
strcpy().

Removes 23 calls to strcpy().

Closes #20067

30 files changed:
lib/Makefile.inc
lib/curl_gethostname.c
lib/curl_gssapi.c
lib/curl_trc.c
lib/curlx/curlx.h
lib/curlx/inet_ntop.c
lib/curlx/strcopy.c [new file with mode: 0644]
lib/curlx/strcopy.h [new file with mode: 0644]
lib/curlx/strerr.c
lib/curlx/winapi.c
lib/hostip.c
lib/hsts.c
lib/imap.c
lib/mime.c
lib/progress.c
lib/smb.c
lib/strdup.h
lib/strerror.c
lib/tftp.c
lib/urlapi.c
lib/vquic/curl_osslq.c
lib/vtls/openssl.c
lib/vtls/vtls.c
lib/vtls/wolfssl.c
lib/ws.c
src/Makefile.inc
src/tool_main.c
src/tool_progress.c
tests/libtest/Makefile.inc
tests/server/Makefile.inc

index 47358a1f871532eb10436d46a1c06c4fda88dfeb..402a5be229fb921e00fc7c973d38c35f2c449afc 100644 (file)
@@ -31,6 +31,7 @@ LIB_CURLX_CFILES = \
   curlx/inet_pton.c \
   curlx/multibyte.c \
   curlx/nonblock.c \
+  curlx/strcopy.c  \
   curlx/strerr.c   \
   curlx/strparse.c \
   curlx/timediff.c \
@@ -51,6 +52,7 @@ LIB_CURLX_HFILES = \
   curlx/multibyte.h \
   curlx/nonblock.h \
   curlx/snprintf.h \
+  curlx/strcopy.h  \
   curlx/strerr.h   \
   curlx/strparse.h \
   curlx/timediff.h \
index e339e6079882ab27e069f339c0b0e45c6637021e..4d772cb4915357114964415c9b796d1e780b33b2 100644 (file)
@@ -23,8 +23,8 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
-
 #include "curl_gethostname.h"
+#include "curlx/strcopy.h"
 
 /*
  * Curl_gethostname() is a wrapper around gethostname() which allows
@@ -60,7 +60,7 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
   const char *force_hostname = getenv("CURL_GETHOSTNAME");
   if(force_hostname) {
     if(strlen(force_hostname) < (size_t)namelen)
-      strcpy(name, force_hostname);
+      curlx_strcopy(name, namelen, force_hostname, strlen(force_hostname));
     else
       return 1; /* cannot do it */
     err = 0;
index efd293f15ec3e613ace0c65d06c05c3f63f48679..2145b42686d3e0c3aba836287f8d0f971fd27bae 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "curl_gssapi.h"
 #include "curl_trc.h"
+#include "curlx/strcopy.h"
 
 #ifdef DEBUGBUILD
 #if defined(HAVE_GSSGNU) || !defined(_WIN32)
@@ -224,7 +225,7 @@ stub_gss_init_sec_context(OM_uint32 *min,
       return GSS_S_FAILURE;
     }
 
-    strcpy(ctx->creds, creds);
+    curlx_strcopy(ctx->creds, sizeof(ctx->creds), creds, strlen(creds));
     ctx->flags = req_flags;
   }
 
index 97a4c23960d9459e3ff1a95359b1909e45885cf1..fd80a76ee5a7eff709b9fe8b4b324056d4999cbd 100644 (file)
@@ -43,6 +43,7 @@
 #include "curlx/strparse.h"
 #include "vtls/vtls.h"
 #include "vquic/vquic.h"
+#include "curlx/strcopy.h"
 
 static void trc_write(struct Curl_easy *data, curl_infotype type,
                       const char *ptr, size_t size)
@@ -184,7 +185,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
     len = curl_mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
 
     if(data->set.errorbuffer && !data->state.errorbuf) {
-      strcpy(data->set.errorbuffer, error);
+      curlx_strcopy(data->set.errorbuffer, CURL_ERROR_SIZE, error, len);
       data->state.errorbuf = TRUE; /* wrote error string */
     }
     error[len++] = '\n';
index 67219603f96f4fad3baac378ce1a92b2bdb9cbb2..817acb07cde9e5000cadd46af5d04ef314988333 100644 (file)
@@ -55,6 +55,9 @@
 #include "strparse.h"
 /* The curlx_str_* parsing functions */
 
+#include "strcopy.h"
+/* curlx_strcopy */
+
 #include "dynbuf.h"
 /* The curlx_dyn_* functions */
 
index 96c58273e2a1dac777c322530c0d71e257b8e091..e3dfdc93d186ebf5e5c342ff28df2e7e6a2e2610 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "inet_ntop.h"
 #include "snprintf.h"
+#include "strcopy.h"
 
 #define IN6ADDRSZ       16
 /* #define INADDRSZ         4 */
@@ -78,7 +79,7 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
 #endif
     return NULL;
   }
-  strcpy(dst, tmp);
+  curlx_strcopy(dst, size, tmp, len);
   return dst;
 }
 
@@ -183,8 +184,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
     *tp++ = ':';
   *tp++ = '\0';
 
-  /* Check for overflow, copy, and we are done.
-   */
+  /* Check for overflow, copy, and we are done. */
   if((size_t)(tp - tmp) > size) {
 #ifdef USE_WINSOCK
     errno = WSAEINVAL;
@@ -193,7 +193,8 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
 #endif
     return NULL;
   }
-  strcpy(dst, tmp);
+
+  curlx_strcopy(dst, size, tmp, strlen(tmp));
   return dst;
 }
 
diff --git a/lib/curlx/strcopy.c b/lib/curlx/strcopy.c
new file mode 100644 (file)
index 0000000..7ca1b9a
--- /dev/null
@@ -0,0 +1,49 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+#include "strcopy.h"
+
+/*
+ * curlx_strcopy() is a replacement for strcpy().
+ *
+ * Provide the target buffer @dest and size of the target buffer @dsize, If
+ * the source string @src with its *string length* @slen fits in the target
+ * buffer it will be copied there - including storing a null terminator.
+ *
+ * If the target buffer is too small, the copy is not performed but if the
+ * target buffer has a non-zero size it will get a null terminator stored.
+ */
+void curlx_strcopy(char *dest,      /* destination buffer */
+                   size_t dsize,    /* size of target buffer */
+                   const char *src, /* source string */
+                   size_t slen)     /* length of source string to copy */
+{
+  DEBUGASSERT(slen < dsize);
+  if(slen < dsize) {
+    memcpy(dest, src, slen);
+    dest[slen] = 0;
+  }
+  else if(dsize)
+    dest[0] = 0;
+}
diff --git a/lib/curlx/strcopy.h b/lib/curlx/strcopy.h
new file mode 100644 (file)
index 0000000..d671149
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef HEADER_CURLX_STRCOPY_H
+#define HEADER_CURLX_STRCOPY_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+void curlx_strcopy(char *dest,
+                   size_t dsize, /* size of target buffer */
+                   const char *src,
+                   size_t slen); /* length of string to copy */
+
+#endif /* HEADER_CURLX_STRCOPY_H */
index a971875a6985ec33b84a21e1678e011dddcbd5c4..addafd2897b6d814cab79748e9753aa3d945135d 100644 (file)
@@ -35,6 +35,7 @@
 #include "winapi.h"
 #include "snprintf.h"
 #include "strerr.h"
+#include "strcopy.h"
 
 #ifdef USE_WINSOCK
 /* This is a helper function for curlx_strerror that converts Winsock error
@@ -224,8 +225,7 @@ static const char *get_winsock_error(int err, char *buf, size_t len)
     return NULL;
   }
   alen = strlen(p);
-  if(alen < len)
-    strcpy(buf, p);
+  curlx_strcopy(buf, len, p, alen);
   return buf;
 #endif
 }
index 3243f7cb4ad32646916363b4e7e4b24f1928a5b3..b46a5bf0ddf0b4962111f412333155525355f59c 100644 (file)
@@ -30,6 +30,7 @@
 #ifdef _WIN32
 #include "winapi.h"
 #include "snprintf.h"
+#include "strcopy.h"
 
 /* This is a helper function for curlx_strerror that converts Windows API error
  * codes (GetLastError) to error messages.
@@ -93,8 +94,7 @@ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen)
 #else
   {
     const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
-    if(strlen(txt) < buflen)
-      strcpy(buf, txt);
+    curlx_strcopy(buf, buflen, txt, strlen(txt));
   }
 #endif
 
index 2cf8d4bb1ed83d7f08189c5790df39fe3cf74d18..aba12ef791ff2f1e5ed6eed96fb4522d8589d883 100644 (file)
@@ -60,6 +60,7 @@
 #include "select.h"
 #include "strcase.h"
 #include "easy_lock.h"
+#include "curlx/strcopy.h"
 #include "curlx/strparse.h"
 
 #if defined(CURLRES_SYNCH) &&                   \
@@ -622,7 +623,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name)
   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
   memcpy(ca->ai_addr, &sa6, ss_size);
   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
-  strcpy(ca->ai_canonname, name);
+  curlx_strcopy(ca->ai_canonname, hostlen + 1, name, hostlen);
   return ca;
 }
 #else
@@ -659,7 +660,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name)
   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
   memcpy(ca->ai_addr, &sa, ss_size);
   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
-  strcpy(ca->ai_canonname, name);
+  curlx_strcopy(ca->ai_canonname, hostlen + 1, name, hostlen);
 
   ca6 = get_localhost6(port, name);
   if(!ca6)
index 393572a6c2efa83238fad47aeab3e151a76ad8b5..a4e233e61e9fb33eedf2fc7fd76d05687c2ae662 100644 (file)
@@ -37,6 +37,7 @@
 #include "curl_share.h"
 #include "curlx/strparse.h"
 #include "curlx/timeval.h"
+#include "curlx/strcopy.h"
 
 #define MAX_HSTS_LINE    4095
 #define MAX_HSTS_HOSTLEN 2048
@@ -294,7 +295,7 @@ static CURLcode hsts_push(struct Curl_easy *data,
                    stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
   }
   else
-    strcpy(e.expire, UNLIMITED);
+    curlx_strcopy(e.expire, sizeof(e.expire), UNLIMITED, strlen(UNLIMITED));
 
   sc = data->set.hsts_write(data, &e, i, data->set.hsts_write_userp);
   *stop = (sc != CURLSTS_OK);
index 20455a957a6eaafe8e4e8369b72894fa564d70f7..c5da40c4e45ee505dc531b7642aacf5f0bc18621 100644 (file)
@@ -73,7 +73,7 @@
 #include "url.h"
 #include "bufref.h"
 #include "curl_sasl.h"
-
+#include "curlx/strcopy.h"
 
 /* meta key for storing protocol meta at easy handle */
 #define CURL_META_IMAP_EASY   "meta:proto:imap:easy"
@@ -1679,7 +1679,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done)
   imap_state(data, imapc, IMAP_SERVERGREET);
 
   /* Start off with an response id of '*' */
-  strcpy(imapc->resptag, "*");
+  curlx_strcopy(imapc->resptag, sizeof(imapc->resptag), "*", 1);
 
   result = imap_multi_statemach(data, done);
 
index b6b64fd10fae6783b7d79c1e1fe20b65cbafa304..011201a7cb8268b5a03ba06cb31bbe389c8ced54 100644 (file)
@@ -32,6 +32,7 @@ struct Curl_easy;
 #include "curl_trc.h"
 #include "transfer.h"
 #include "strdup.h"
+#include "curlx/strcopy.h"
 #include "curlx/fopen.h"
 #include "curlx/base64.h"
 
@@ -602,7 +603,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
         }
       }
       if(softlinebreak) {
-        strcpy(buf, "\x3D\x0D\x0A");    /* "=\r\n" */
+        curlx_strcopy(buf, sizeof(buf), "\x3D\x0D\x0A", 3);    /* "=\r\n" */
         len = 3;
         consumed = 0;
       }
index 18dde191632e7335370c1bbe0ae666db245f8fa4..fc0164c1725ac145da5ee70db20d5661ad7d8beb 100644 (file)
@@ -30,6 +30,7 @@
 #include "progress.h"
 #include "transfer.h"
 #include "curlx/timeval.h"
+#include "curlx/strcopy.h"
 
 /* check rate limits within this many recent milliseconds, at minimum. */
 #define MIN_RATE_LIMIT_PERIOD 3000
 #ifndef CURL_DISABLE_PROGRESS_METER
 /* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
    byte) */
-static void time2str(char *r, curl_off_t seconds)
+static void time2str(char *r, size_t rsize, curl_off_t seconds)
 {
   curl_off_t h;
   if(seconds <= 0) {
-    strcpy(r, "--:--:--");
+    curlx_strcopy(r, rsize, "--:--:--", 8);
     return;
   }
   h = seconds / 3600;
   if(h <= 99) {
     curl_off_t m = (seconds - (h * 3600)) / 60;
     curl_off_t s = (seconds - (h * 3600)) - (m * 60);
-    curl_msnprintf(r, 9, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T,
+    curl_msnprintf(r, rsize, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T,
                    h, m, s);
   }
   else {
@@ -57,21 +58,21 @@ static void time2str(char *r, curl_off_t seconds)
     curl_off_t d = seconds / 86400;
     h = (seconds - (d * 86400)) / 3600;
     if(d <= 999)
-      curl_msnprintf(r, 9, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h);
+      curl_msnprintf(r, rsize, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h);
     else
-      curl_msnprintf(r, 9, "%7" FMT_OFF_T "d", d);
+      curl_msnprintf(r, rsize, "%7" FMT_OFF_T "d", d);
   }
 }
 
 /* The point of this function would be to return a string of the input data,
    but never longer than 6 columns (+ one zero byte).
    Add suffix k, M, G when suitable... */
-static char *max6data(curl_off_t bytes, char *max6)
+static char *max6out(curl_off_t bytes, char *max6, size_t mlen)
 {
   /* a signed 64-bit value is 8192 petabytes maximum, shown as
      8.0E (exabytes)*/
   if(bytes < 100000)
-    curl_msnprintf(max6, 7, "%6" CURL_FORMAT_CURL_OFF_T, bytes);
+    curl_msnprintf(max6, mlen, "%6" CURL_FORMAT_CURL_OFF_T, bytes);
   else {
     const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 };
     int k = 0;
@@ -85,7 +86,7 @@ static char *max6data(curl_off_t bytes, char *max6)
       DEBUGASSERT(unit[k]);
     } while(unit[k]);
     /* xxx.yU */
-    curl_msnprintf(max6, 7, "%3" CURL_FORMAT_CURL_OFF_T
+    curl_msnprintf(max6, mlen, "%3" CURL_FORMAT_CURL_OFF_T
                    ".%" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
                    (bytes % 1024) / (1024 / 10), unit[k]);
   }
@@ -528,9 +529,10 @@ static void progress_meter(struct Curl_easy *data)
   /* Since both happen at the same time, total expected duration is max. */
   total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs);
   /* create the three time strings */
-  time2str(time_left, total_estm.secs > 0 ? (total_estm.secs - cur_secs) : 0);
-  time2str(time_total, total_estm.secs);
-  time2str(time_spent, cur_secs);
+  time2str(time_left, sizeof(time_left),
+           total_estm.secs > 0 ? (total_estm.secs - cur_secs) : 0);
+  time2str(time_total, sizeof(time_total), total_estm.secs);
+  time2str(time_spent, sizeof(time_spent), cur_secs);
 
   /* Get the total amount of data expected to get transferred */
   total_expected_size = p->ul_size_known ? p->ul.total_size : p->ul.cur_size;
@@ -554,18 +556,24 @@ static void progress_meter(struct Curl_easy *data)
                 "%3" FMT_OFF_T " %s "
                 "%3" FMT_OFF_T " %s "
                 "%3" FMT_OFF_T " %s %s %s  %s %s %s %s",
-                total_estm.percent, /* 3 letters */         /* total % */
-                max6data(total_expected_size, max6[2]),     /* total size */
-                dl_estm.percent, /* 3 letters */            /* rcvd % */
-                max6data(p->dl.cur_size, max6[0]),          /* rcvd size */
-                ul_estm.percent, /* 3 letters */            /* xfer % */
-                max6data(p->ul.cur_size, max6[1]),          /* xfer size */
-                max6data(p->dl.speed, max6[3]),             /* avrg dl speed */
-                max6data(p->ul.speed, max6[4]),             /* avrg ul speed */
-                time_total,    /* 8 letters */              /* total time */
-                time_spent,    /* 8 letters */              /* time spent */
-                time_left,     /* 8 letters */              /* time left */
-                max6data(p->current_speed, max6[5])
+                total_estm.percent, /* 3 letters */    /* total % */
+                max6out(total_expected_size, max6[2],
+                        sizeof(max6[2])),              /* total size */
+                dl_estm.percent, /* 3 letters */       /* rcvd % */
+                max6out(p->dl.cur_size, max6[0],
+                        sizeof(max6[0])),              /* rcvd size */
+                ul_estm.percent, /* 3 letters */       /* xfer % */
+                max6out(p->ul.cur_size, max6[1],
+                        sizeof(max6[1])),              /* xfer size */
+                max6out(p->dl.speed, max6[3],
+                        sizeof(max6[3])),              /* avrg dl speed */
+                max6out(p->ul.speed, max6[4],
+                        sizeof(max6[4])),              /* avrg ul speed */
+                time_total,    /* 8 letters */         /* total time */
+                time_spent,    /* 8 letters */         /* time spent */
+                time_left,     /* 8 letters */         /* time left */
+                max6out(p->current_speed, max6[5],
+                        sizeof(max6[5]))               /* current speed */
     );
 
   /* we flush the output stream to make it appear as soon as possible */
index d9eeec83a5445f7d3a5e6358ab4f75af2772e822..5f3e419a420b2bf3002f213f479ae9a5b572921d 100644 (file)
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -40,7 +40,7 @@
 #include "curl_ntlm_core.h"
 #include "escape.h"
 #include "curl_endian.h"
-
+#include "curlx/strcopy.h"
 
 /* meta key for storing protocol meta at easy handle */
 #define CURL_META_SMB_EASY   "meta:proto:smb:easy"
@@ -790,7 +790,7 @@ static CURLcode smb_send_open(struct Curl_easy *data,
     msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
   }
   msg.byte_count = smb_swap16((unsigned short)byte_count);
-  strcpy(msg.bytes, req->path);
+  curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1);
 
   return smb_send_message(data, smbc, req, SMB_COM_NT_CREATE_ANDX, &msg,
                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
index 22ade4d7ffad67d06e5b272dc9229bf570c1bd75..cd01ffbdb8949acb3a7cad8376ec840b23c75d93 100644 (file)
@@ -34,5 +34,4 @@ wchar_t *Curl_wcsdup(const wchar_t *src);
 void *Curl_memdup(const void *src, size_t buffer_length);
 void *Curl_saferealloc(void *ptr, size_t size);
 void *Curl_memdup0(const char *src, size_t length);
-
 #endif /* HEADER_CURL_STRDUP_H */
index a15a8d01b4952b94560fca4cfa1393fb1885bcec..6c655f4fe65e13406a3ec09fcbd6680afd266d1c 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "curlx/winapi.h"
 #include "strerror.h"
+#include "curlx/strcopy.h"
 
 const char *curl_easy_strerror(CURLcode error)
 {
@@ -663,8 +664,7 @@ const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen)
     txt = "No error";
   else
     txt = "Error";
-  if(buflen > strlen(txt))
-    strcpy(buf, txt);
+  curlx_strcopy(buf, buflen, txt, strlen(txt));
 #endif
 
   if(errno != old_errno)
index d457611662b0202aac228e048d5d3149e8c81d12..0e03933ab9a9549125b3929d484c2a017a65f549 100644 (file)
@@ -62,6 +62,7 @@
 #include "escape.h"
 #include "curlx/strerr.h"
 #include "curlx/strparse.h"
+#include "curlx/strcopy.h"
 
 /* RFC2348 allows the block size to be negotiated */
 #define TFTP_BLKSIZE_DEFAULT 512
@@ -369,12 +370,17 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state,
 }
 
 static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize,
-                                char *buf, const char *option)
+                                size_t index, const char *option)
 {
-  if((strlen(option) + *csize + 1) > (size_t)state->blksize)
+  char *buf = (char *)&state->spacket.data[index];
+  size_t oplen = strlen(option);
+  size_t blen;
+  if((state->blksize <= index) ||
+     (oplen + 1) > (size_t)(state->blksize - index))
     return CURLE_TFTP_ILLEGAL;
-  strcpy(buf, option);
-  *csize += strlen(option) + 1;
+  blen = state->blksize - index;
+  curlx_strcopy(buf, blen, option, oplen);
+  *csize += oplen + 1;
   return CURLE_OK;
 }
 
@@ -479,32 +485,23 @@ static CURLcode tftp_send_first(struct tftp_conn *state,
                      data->state.upload && (data->state.infilesize != -1) ?
                      data->state.infilesize : 0);
 
-      result = tftp_option_add(state, &sbytes,
-                               (char *)state->spacket.data + sbytes,
-                               TFTP_OPTION_TSIZE);
+      result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE);
       if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes,
-                                 (char *)state->spacket.data + sbytes, buf);
+        result = tftp_option_add(state, &sbytes, sbytes, buf);
 
       /* add blksize option */
       curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
       if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes,
-                                 (char *)state->spacket.data + sbytes,
-                                 TFTP_OPTION_BLKSIZE);
+        result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE);
       if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes,
-                                 (char *)state->spacket.data + sbytes, buf);
+        result = tftp_option_add(state, &sbytes, sbytes, buf);
 
       /* add timeout option */
       curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time);
       if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes,
-                                 (char *)state->spacket.data + sbytes,
-                                 TFTP_OPTION_INTERVAL);
+        result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL);
       if(result == CURLE_OK)
-        result = tftp_option_add(state, &sbytes,
-                                 (char *)state->spacket.data + sbytes, buf);
+        result = tftp_option_add(state, &sbytes, sbytes, buf);
 
       if(result != CURLE_OK) {
         failf(data, "TFTP buffer too small for options");
index f84ee2ed09d2f3d68e7282f3cfd14e677f6db5ff..747ac814bae174c679b158558e3bb13170969589 100644 (file)
@@ -509,7 +509,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname,
     hostname[hlen] = 0; /* end the address there */
     if(curlx_inet_pton(AF_INET6, hostname, dest) != 1)
       return CURLUE_BAD_IPV6;
-    if(curlx_inet_ntop(AF_INET6, dest, hostname, hlen)) {
+    if(curlx_inet_ntop(AF_INET6, dest, hostname, hlen + 1)) {
       hlen = strlen(hostname); /* might be shorter now */
       hostname[hlen + 1] = 0;
     }
index b2394d287c0213fc29779ba38cda44f317a32b36..cffe61a4022d35c4d0360cc54526ae95e9b0b9a4 100644 (file)
@@ -53,6 +53,7 @@
 #include "../url.h"
 #include "../bufref.h"
 #include "../curlx/strerr.h"
+#include "../curlx/strcopy.h"
 
 /* A stream window is the maximum amount we need to buffer for
  * each active transfer. We use HTTP/3 flow control and only ACK
@@ -141,7 +142,7 @@ static char *osslq_strerror(unsigned long error, char *buf, size_t size)
   if(!*buf) {
     const char *msg = error ? "Unknown error" : "No error";
     if(strlen(msg) < size)
-      strcpy(buf, msg);
+      curlx_strcopy(buf, size, msg, strlen(msg));
   }
 
   return buf;
index 99ee20974c0163adbd1f3066f5ceff2580ce543c..dcc76a9299aaeb505e605c911cc16afc30c72ae0 100644 (file)
@@ -61,6 +61,7 @@
 #include "../multiif.h"
 #include "../curlx/strerr.h"
 #include "../curlx/strparse.h"
+#include "../curlx/strcopy.h"
 #include "../strdup.h"
 #include "apple.h"
 
@@ -773,8 +774,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
 
   if(!*buf) {
     const char *msg = error ? "Unknown error" : "No error";
-    if(strlen(msg) < size)
-      strcpy(buf, msg);
+    curlx_strcopy(buf, size, msg, strlen(msg));
   }
 
   return buf;
index ed63842b3630a210b2b25952d61df28e0b751d3a..78f17ea1328e38650bfcac0798808affee720121 100644 (file)
@@ -72,6 +72,7 @@
 #include "../select.h"
 #include "../setopt.h"
 #include "../strdup.h"
+#include "../curlx/strcopy.h"
 
 #ifdef USE_APPLE_SECTRUST
 #include <Security/Security.h>
@@ -1082,10 +1083,7 @@ static size_t multissl_version(char *buffer, size_t size)
   }
 
   if(size) {
-    if(backends_len < size)
-      strcpy(buffer, backends);
-    else
-      *buffer = 0; /* did not fit */
+    curlx_strcopy(buffer, size, backends, backends_len);
   }
   return 0;
 }
index 9f75b54df39a6287ee87e30f8d3c170aa28f9e58..3ccd0917a4fe19047136f238090128c54663db43 100644 (file)
@@ -62,6 +62,7 @@
 #include "../connect.h" /* for the connect timeout */
 #include "../progress.h"
 #include "../strdup.h"
+#include "../curlx/strcopy.h"
 #include "x509asn1.h"
 
 #include <wolfssl/ssl.h>
@@ -1542,8 +1543,7 @@ static char *wssl_strerror(unsigned long error, char *buf, unsigned long size)
 
   if(!*buf) {
     const char *msg = error ? "Unknown error" : "No error";
-    /* the string fits because the assert above assures this */
-    strcpy(buf, msg);
+    curlx_strcopy(buf, size, msg, strlen(msg));
   }
 
   return buf;
index c97586ec02e31618a3602140fe978531ced10a61..0f60d442136940193da4b7e0d5e0d575b678e6ef 100644 (file)
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -40,6 +40,7 @@
 #include "transfer.h"
 #include "select.h"
 #include "curlx/strparse.h"
+#include "curlx/strcopy.h"
 
 /***
     RFC 6455 Section 5.2
@@ -1274,7 +1275,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
     curlx_free(randstr);
     return CURLE_FAILED_INIT;
   }
-  strcpy(keyval, randstr);
+  curlx_strcopy(keyval, sizeof(keyval), randstr, randlen);
   curlx_free(randstr);
   for(i = 0; !result && (i < CURL_ARRAYSIZE(heads)); i++) {
     if(!Curl_checkheaders(data, heads[i].name, strlen(heads[i].name))) {
index 82c3817ce9da9a4c48c0e8cd91d0511806b7e022..4e8efdd9265bee9d36800f1d7bc591811d23f8bf 100644 (file)
@@ -38,6 +38,7 @@ CURLX_CFILES = \
   ../lib/curlx/fopen.c \
   ../lib/curlx/multibyte.c \
   ../lib/curlx/nonblock.c \
+  ../lib/curlx/strcopy.c \
   ../lib/curlx/strerr.c \
   ../lib/curlx/strparse.c \
   ../lib/curlx/timediff.c \
@@ -55,6 +56,7 @@ CURLX_HFILES = \
   ../lib/curlx/multibyte.h \
   ../lib/curlx/nonblock.h \
   ../lib/curlx/snprintf.h \
+  ../lib/curlx/strcopy.h \
   ../lib/curlx/strerr.h \
   ../lib/curlx/strparse.h \
   ../lib/curlx/timediff.h \
index 3784f280f645005478c149e6864961a533db303f..e4a5467de76d0703caf10502a4a57ad71f15e2d0 100644 (file)
@@ -108,9 +108,7 @@ static void memory_tracking_init(void)
   if(env) {
     /* use the value as filename */
     char fname[512];
-    if(strlen(env) >= sizeof(fname))
-      env[sizeof(fname) - 1] = '\0';
-    strcpy(fname, env);
+    curlx_strcopy(fname, sizeof(fname), env, strlen(env));
     curl_free(env);
     curl_dbg_memdebug(fname);
     /* this weird stuff here is to make curl_free() get called before
index 53ed9bcb3cec00d452a1ce223e5064d8b02d1e7f..5ed038917e17467eb04d6f3adf7f86c30c775982 100644 (file)
 /* The point of this function would be to return a string of the input data,
    but never longer than 5 columns (+ one zero byte).
    Add suffix k, M, G when suitable... */
-static char *max5data(curl_off_t bytes, char *max5)
+static char *max5data(curl_off_t bytes, char *max5, size_t mlen)
 {
   /* a signed 64-bit value is 8192 petabytes maximum */
-  const char unit[] = { 'k', 'M', 'G', 'T', 'P', 0 };
+  const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 };
   int k = 0;
   if(bytes < 100000) {
-    curl_msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
+    curl_msnprintf(max5, mlen, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
     return max5;
   }
 
@@ -43,14 +43,14 @@ static char *max5data(curl_off_t bytes, char *max5)
     curl_off_t nbytes = bytes / 1024;
     if(nbytes < 100) {
       /* display with a decimal */
-      curl_msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
+      curl_msnprintf(max5, mlen, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
                      CURL_FORMAT_CURL_OFF_T "%c", bytes / 1024,
                      (bytes % 1024) / (1024 / 10), unit[k]);
       break;
     }
     else if(nbytes < 10000) {
       /* no decimals */
-      curl_msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
+      curl_msnprintf(max5, mlen, "%4" CURL_FORMAT_CURL_OFF_T "%c", nbytes,
                      unit[k]);
       break;
     }
@@ -87,18 +87,18 @@ int xferinfo_cb(void *clientp,
 
 /* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
    byte) */
-static void time2str(char *r, curl_off_t seconds)
+static void time2str(char *r, size_t rlen, curl_off_t seconds)
 {
   curl_off_t h;
   if(seconds <= 0) {
-    strcpy(r, "--:--:--");
+    curlx_strcopy(r, rlen, "--:--:--", 8);
     return;
   }
   h = seconds / 3600;
   if(h <= 99) {
     curl_off_t m = (seconds - (h * 3600)) / 60;
     curl_off_t s = (seconds - (h * 3600)) - (m * 60);
-    curl_msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T
+    curl_msnprintf(r, rlen, "%2" CURL_FORMAT_CURL_OFF_T
                    ":%02" CURL_FORMAT_CURL_OFF_T
                    ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s);
   }
@@ -108,10 +108,10 @@ static void time2str(char *r, curl_off_t seconds)
     curl_off_t d = seconds / 86400;
     h = (seconds - (d * 86400)) / 3600;
     if(d <= 999)
-      curl_msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
+      curl_msnprintf(r, rlen, "%3" CURL_FORMAT_CURL_OFF_T
                      "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
     else
-      curl_msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
+      curl_msnprintf(r, rlen, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
   }
 }
 
@@ -254,14 +254,14 @@ bool progress_meter(CURLM *multi, struct curltime *start, bool final)
     if(dlknown && speed) {
       curl_off_t est = all_dltotal / speed;
       curl_off_t left = (all_dltotal - all_dlnow) / speed;
-      time2str(time_left, left);
-      time2str(time_total, est);
+      time2str(time_left, sizeof(time_left), left);
+      time2str(time_total, sizeof(time_total), est);
     }
     else {
-      time2str(time_left, 0);
-      time2str(time_total, 0);
+      time2str(time_left, sizeof(time_left), 0);
+      time2str(time_total, sizeof(time_total), 0);
     }
-    time2str(time_spent, spent);
+    time2str(time_spent, sizeof(time_spent), spent);
 
     (void)curl_multi_get_offt(multi, CURLMINFO_XFERS_ADDED, &xfers_added);
     (void)curl_multi_get_offt(multi, CURLMINFO_XFERS_RUNNING, &xfers_running);
@@ -281,14 +281,14 @@ bool progress_meter(CURLM *multi, struct curltime *start, bool final)
 
                   dlpercen,  /* 3 letters */
                   ulpercen,  /* 3 letters */
-                  max5data(all_dlnow, buffer[0]),
-                  max5data(all_ulnow, buffer[1]),
+                  max5data(all_dlnow, buffer[0], sizeof(buffer[0])),
+                  max5data(all_ulnow, buffer[1], sizeof(buffer[1])),
                   xfers_added,
                   xfers_running,
                   time_total,
                   time_spent,
                   time_left,
-                  max5data(speed, buffer[2]), /* speed */
+                  max5data(speed, buffer[2], sizeof(buffer[2])), /* speed */
                   final ? "\n" : "");
     return TRUE;
   }
index ab616ca67a116a4af315844a1041f86fe0d42189..e365e4e6f9801a2237227e2c545967fb83d3328e 100644 (file)
@@ -37,6 +37,7 @@ CURLX_C = \
   ../../lib/curl_threads.c \
   ../../lib/curlx/fopen.c \
   ../../lib/curlx/multibyte.c \
+  ../../lib/curlx/strcopy.c \
   ../../lib/curlx/strerr.c \
   ../../lib/curlx/strparse.c \
   ../../lib/curlx/timediff.c \
index eb81a3494998c7c6da3ba04364421021cc13b879..96da26d64308c6eab49f1efa4a99dac9169a8bb8 100644 (file)
@@ -40,6 +40,7 @@ CURLX_C = \
   ../../lib/curlx/inet_pton.c \
   ../../lib/curlx/multibyte.c \
   ../../lib/curlx/nonblock.c \
+  ../../lib/curlx/strcopy.c \
   ../../lib/curlx/strerr.c \
   ../../lib/curlx/strparse.c \
   ../../lib/curlx/timediff.c \