Tags: patch

Further to my prior report it is not the use of variable length array
but the overwriting the end of the array that is the problem. This bug
is repeated multiple times throughout the source file src/detector.c.
It appears that there have been attempts to fix a couple of
occurrences of the bug but even that has been cocked up. There does
not seem to be any understanding by the authors of the code that
strlen() returns the length of a string less the terminating zero.

It is astonishing that this code successfully runs on any architecture
whatsoever given the multiple array overruns!

I attach a patch that fixes many occurrences of the bug.  This is
sufficient to get ohcount running on itself on arm64 without crashing.

There are also occurrences of array underruns in the code. For example,
about line 615 of src/detector.c there is the following:

    p = line;
    while (p < eol) {
      if (islower(*p) && p != bof && !isalnum(*(p - 1)) && *(p - 1) != '_') {

On the first iteration of the while loop in the if statement one byte
before the start of array line can be read.  That is undefined
behaviour.  I have not fixed this in the attached patch, but it should
be reported upstream.  Frankly, the code needs a full audit for issues
such as these.

Regards,
Michael.
--- a/src/detector.c
+++ b/src/detector.c
@@ -46,7 +46,7 @@
       while (isalnum(*pe)) pe++;
       length = pe - p;
       strncpy(buf, p, length);
-      buf[length] = '\0';
+      buf[79] = '\0';
       struct LanguageMap *rl = ohcount_hash_language_from_name(buf, length);
       if (rl) return(rl->name);
     }
@@ -63,7 +63,7 @@
     } while (*p == '-'); // Skip over any switches.
     length = pe - p;
     strncpy(buf, p, length);
-    buf[length] = '\0';
+    buf[79] = '\0';
     struct LanguageMap *rl = ohcount_hash_language_from_name(buf, length);
     if (rl) return(rl->name);
   } else if (strstr(line, "xml")) return(LANG_XML);
@@ -146,7 +146,7 @@
     while (pe < eof) {
       // Get the contents of the first line.
       while (pe < eof && *pe != '\r' && *pe != '\n') pe++;
-      length = (pe - p <= sizeof(line)) ? pe - p : sizeof(line);
+      length = (pe - p <= sizeof(line)-1) ? pe - p : sizeof(line)-1;
       strncpy(line, p, length);
       line[length] = '\0';
       if (*line == '#' && *(line + 1) == '!') {
@@ -166,7 +166,7 @@
       }
       pe = p;
       while (!isspace(*pe) && *pe != ';' && pe != strstr(pe, "-*-")) pe++;
-      length = (pe - p <= sizeof(buf)) ? pe - p : sizeof(buf);
+      length = (pe - p <= sizeof(buf)-1) ? pe - p : sizeof(buf)-1;
       strncpy(buf, p, length);
       buf[length] = '\0';
 
@@ -236,7 +236,7 @@
     if (p && pe) {
       p += 3;
       const int length = pe - p;
-      char buf[length];
+      char buf[length+1];
       strncpy(buf, p, length);
       buf[length] = '\0';
       char *eol = buf + strlen(buf);
@@ -550,7 +550,7 @@
   // If the directory contains a matching *.m file, likely Objective C.
   length = strlen(sourcefile->filename);
   if (strcmp(sourcefile->ext, "h") == 0) {
-    char path[length];
+    char path[length+1];
     strncpy(path, sourcefile->filename, length);
     path[length] = '\0';
     *(path + length - 1) = 'm';
@@ -572,7 +572,7 @@
   while (pe < eof) {
     // Get a line at a time.
     while (pe < eof && *pe != '\r' && *pe != '\n') pe++;
-    length = (pe - p <= sizeof(line)) ? pe - p : sizeof(line);
+    length = (pe - p <= sizeof(line)-1) ? pe - p : sizeof(line)-1;
     strncpy(line, p, length);
     line[length] = '\0';
     char *eol = line + strlen(line);
@@ -594,7 +594,7 @@
           while (pe < eol && *pe != '>' && *pe != '"') pe++;
           length = pe - p;
           strncpy(buf, p, length);
-          buf[length] = '\0';
+          buf[80] = '\0';
           if (ohcount_hash_is_cppheader(buf, length))
             return LANG_CPP;
           // Is the extension for the header file a C++ file?
@@ -602,7 +602,7 @@
           while (p > line && *(p - 1) != '.') p--;
           length = pe - p;
           strncpy(buf, p, length);
-          buf[length] = '\0';
+          buf[80] = '\0';
           struct ExtensionMap *re = ohcount_hash_language_from_ext(buf, length);
           if (re && strcmp(re->value, LANG_CPP) == 0)
             return LANG_CPP;
@@ -619,7 +619,7 @@
         if (!isalnum(*pe) && *pe != '_') {
           length = pe - p;
           strncpy(buf, p, length);
-          buf[length] = '\0';
+          buf[80] = '\0';
           if (strcmp(buf, "class") == 0 ||
               strcmp(buf, "namespace") == 0 ||
               strcmp(buf, "template") == 0 ||
@@ -650,7 +650,7 @@
   if (strstr(p, ".") <= pe) {
     // Only if the filename has an extension prior to the .in
     length = pe - p;
-    char buf[length];
+    char buf[length+1];
     strncpy(buf, p, length);
     buf[length] = '\0';
     p = ohcount_sourcefile_get_contents(sourcefile);
@@ -741,7 +741,7 @@
   while (pe < eof) {
     // Get a line at a time.
     while (pe < eof && *pe != '\r' && *pe != '\n') pe++;
-    length = (pe - p <= sizeof(line)) ? pe - p : sizeof(line);
+    length = (pe - p <= sizeof(line)-1) ? pe - p : sizeof(line)-1;
     strncpy(line, p, length);
     line[length] = '\0';
     char *eol = line + strlen(line);
@@ -796,7 +796,7 @@
         if (!isalnum(*pe)) {
           length = pe - p;
           strncpy(buf, p, length);
-          buf[length] = '\0';
+          buf[80] = '\0';
           if (strcmp(buf, "end_try_catch") == 0 ||
               strcmp(buf, "end_unwind_protect") == 0 ||
               strcmp(buf, "endfunction") == 0 ||

Reply via email to