From b4c0d01e751975090fa7346a2a71dd778c3b412c Mon Sep 17 00:00:00 2001
From: Hartmut Goebel <h.goebel@crazy-compilers.com>
Date: Sun, 3 May 2020 15:43:52 +0200
Subject: [PATCH] Bootloader: Use snprintf() to simplify some string length
 checks.

Instead of copying strings and checking whether the string si terminated with
a Null bytes, use snprintf, which tells how much space the new string would
have taken and compare this to the available space.

Since snprintf() add some overhead, This code might be a bit less efficient.
But this sie the bootloader, and most of the functions are called once only.
---
 bootloader/src/pyi_archive.c   | 21 ++++++++-------------
 bootloader/src/pyi_launch.c    | 27 +++++++++------------------
 bootloader/src/pyi_main.c      |  4 ++--
 bootloader/src/pyi_path.c      |  3 +--
 bootloader/src/pyi_pythonlib.c | 14 +++++++++-----
 bootloader/src/pyi_utils.c     |  7 ++-----
 6 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/bootloader/src/pyi_archive.c b/bootloader/src/pyi_archive.c
index ed886e09fb..32b129fb98 100644
--- a/bootloader/src/pyi_archive.c
+++ b/bootloader/src/pyi_archive.c
@@ -455,28 +455,23 @@ int
 pyi_arch_set_paths(ARCHIVE_STATUS *status, char const * archivePath,
                    char const * archiveName)
 {
-    size_t pathlen, namelen;
-
-    pathlen = strnlen(archivePath, PATH_MAX);
-    namelen = strnlen(archiveName, PATH_MAX);
-
-    if (pathlen+namelen+1 > PATH_MAX) {
-        return -1;
+    /* Set homepath to where the archive is */
+    if (snprintf(status->homepath, PATH_MAX, "%s", archivePath) >= PATH_MAX) {
+      return -1;
     }
 
     /* Get the archive Path */
-    strcpy(status->archivename, archivePath);
-    strcat(status->archivename, archiveName);
-
-    /* Set homepath to where the archive is */
-    strcpy(status->homepath, archivePath);
+    if (snprintf(status->archivename, PATH_MAX,
+                 "%s%s", archivePath, archiveName) >= PATH_MAX) {
+        return -1;
+    }
 
     /*
      * Initial value of mainpath is homepath. It might be overriden
      * by temppath if it is available.
      */
     status->has_temp_directory = false;
-    strcpy(status->mainpath, status->homepath);
+    strcpy(status->mainpath, status->homepath); // homepath fits into PATH_MAX
 
     return 0;
 }
diff --git a/bootloader/src/pyi_launch.c b/bootloader/src/pyi_launch.c
index 3517be6353..432a43416f 100644
--- a/bootloader/src/pyi_launch.c
+++ b/bootloader/src/pyi_launch.c
@@ -79,15 +79,13 @@ checkFile(char *buf, const char *fmt, ...)
 static int
 splitName(char *path, char *filename, const char *item)
 {
-    char name[PATH_MAX + 1];
+    char name[PATH_MAX];
 
     VS("LOADER: Splitting item into path and filename\n");
-    strncpy(name, item, PATH_MAX + 1);
-
-    if (name[PATH_MAX] != '\0') {
+    if (snprintf(name, PATH_MAX, "%s", item) >= PATH_MAX) {
         return -1;
     }
-
+    // `name` fits into PATH_MAX, so will all substrings
     strcpy(path, strtok(name, ":"));
     strcpy(filename, strtok(NULL, ":"));
 
@@ -148,13 +146,11 @@ _get_archive(ARCHIVE_STATUS *archive_pool[], const char *path)
         return NULL;
     }
 
-    strncpy(archive->archivename, path, PATH_MAX);
-    strncpy(archive->homepath, archive_pool[SELF]->homepath, PATH_MAX);
-    strncpy(archive->temppath, archive_pool[SELF]->temppath, PATH_MAX);
-
-    if (archive->archivename[PATH_MAX-1] != '\0'
-        || archive->homepath[PATH_MAX-1] != '\0'
-        || archive->temppath[PATH_MAX-1] != '\0') {
+    if ((snprintf(archive->archivename, PATH_MAX, "%s", path) >= PATH_MAX) ||
+        (snprintf(archive->homepath, PATH_MAX, "%s",
+                  archive_pool[SELF]->homepath) >= PATH_MAX) ||
+        (snprintf(archive->temppath, PATH_MAX, "%s",
+                  archive_pool[SELF]->temppath) >= PATH_MAX)) {
         FATALERROR("Archive path exceeds PATH_MAX\n");
         free(archive);
         return NULL;
@@ -369,7 +365,6 @@ pyi_launch_run_scripts(ARCHIVE_STATUS *status)
 {
     unsigned char *data;
     char buf[PATH_MAX];
-    size_t namelen;
     TOC * ptoc = status->tocbuff;
     PyObject *__main__;
     PyObject *__file__;
@@ -400,14 +395,10 @@ pyi_launch_run_scripts(ARCHIVE_STATUS *status)
             data = pyi_arch_extract(status, ptoc);
             /* Set the __file__ attribute within the __main__ module,
              *  for full compatibility with normal execution. */
-            namelen = strnlen(ptoc->name, PATH_MAX);
-            if (namelen >= PATH_MAX-strlen(".py")-1) {
+            if (snprintf(buf, PATH_MAX, "%s.py", ptoc->name) >= PATH_MAX) {
                 FATALERROR("Name exceeds PATH_MAX\n");
                 return -1;
             }
-
-            strcpy(buf, ptoc->name);
-            strcat(buf, ".py");
             VS("LOADER: Running %s\n", buf);

             if (is_py2) {
diff --git a/bootloader/src/pyi_main.c b/bootloader/src/pyi_main.c
index 02b1bec5dc..472cd1ae9a 100644
--- a/bootloader/src/pyi_main.c
+++ b/bootloader/src/pyi_main.c
@@ -126,8 +126,8 @@ pyi_main(int argc, char * argv[])
          *  we pass it through status variable
          */
         if (strcmp(homepath, extractionpath) != 0) {
-            strncpy(archive_status->temppath, extractionpath, PATH_MAX);
-            if (archive_status->temppath[PATH_MAX-1] != '\0') {
+            if (snprintf(archive_status->temppath, PATH_MAX,
+                         "%s", extractionpath) >= PATH_MAX) {
                 VS("LOADER: temppath exceeds PATH_MAX\n");
                 return -1;
             }
diff --git a/bootloader/src/pyi_path.c b/bootloader/src/pyi_path.c
index fb417e9269..6ed252ffab 100644
--- a/bootloader/src/pyi_path.c
+++ b/bootloader/src/pyi_path.c
@@ -286,8 +286,7 @@ pyi_path_executable(char *execfile, const char *appname)
         if (-1 == result) {
             /* Searching $PATH failed, user is crazy. */
             VS("LOADER: Searching $PATH failed for %s", appname);
-            strncpy(buffer, appname, PATH_MAX);
-            if (buffer[PATH_MAX-1] != '\0') {
+            if (snprintf(buffer, PATH_MAX, "%s", "appname") >= PATH_MAX) {
                 VS("LOADER: Appname too large %s\n", appname);
                 return -1;
             }
diff --git a/bootloader/src/pyi_pythonlib.c b/bootloader/src/pyi_pythonlib.c
index caffcd6daa..8144885b17 100644
--- a/bootloader/src/pyi_pythonlib.c
+++ b/bootloader/src/pyi_pythonlib.c
@@ -414,18 +414,16 @@ pyi_pylib_start_python(ARCHIVE_STATUS *status)
     };
 
     /* Set sys.path */
-    if (is_py2) {
-        /* sys.path = [mainpath] */
-        strncpy(pypath, status->mainpath, PATH_MAX);
-    }
-    else {
-        /* sys.path = [base_library, mainpath] */
+    /* sys.path = [base_library, mainpath] */
-        strncpy(pypath, status->mainpath, PATH_MAX);
-        strncat(pypath, PYI_SEPSTR, PATH_MAX);
-        strncat(pypath, "base_library.zip", PATH_MAX);
-        strncat(pypath, PYI_PATHSEPSTR, PATH_MAX);
-        strncat(pypath, status->mainpath, PATH_MAX);
-    };
+    if (snprintf(pypath, sizeof pypath, "%s%cbase_library.zip%c%s",
+                 status->mainpath, PYI_SEP, PYI_PATHSEP, status->mainpath)
+        >= sizeof pypath) {
+        // This should never happen, since mainpath is < PATH_MAX and pypath is
+        // huge enough
+        FATALERROR("sys.path (based on %s) exceeds buffer[%d] space\n",
+                   status->mainpath, sizeof pypath);
+        return -1;
+    }
 
     /*
      * On Python 3, we must set sys.path to have base_library.zip before
diff --git a/bootloader/src/pyi_utils.c b/bootloader/src/pyi_utils.c
index e18d611f26..0c89975908 100644
--- a/bootloader/src/pyi_utils.c
+++ b/bootloader/src/pyi_utils.c
@@ -557,11 +557,8 @@ pyi_open_target(const char *path, const char* name_)
     char *dir;
     size_t len;
 
-    strncpy(fnm, path, PATH_MAX);
-    strncpy(name, name_, PATH_MAX);
-
-    /* Check if the path names could be copied */
-    if (fnm[PATH_MAX-1] != '\0' || name[PATH_MAX-1] != '\0') {
+    if (snprintf(fnm, PATH_MAX, "%s", path) >= PATH_MAX ||
+        snprintf(name, PATH_MAX, "%s", name_) >= PATH_MAX) {
         return NULL;
     }
 
