Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heap overflow with empty global pawndoc comments #700

Open
Y-Less opened this issue Mar 7, 2022 · 3 comments
Open

Heap overflow with empty global pawndoc comments #700

Y-Less opened this issue Mar 7, 2022 · 3 comments

Comments

@Y-Less
Copy link
Member

Y-Less commented Mar 7, 2022

Issue description:

If you have a lot of stand-alone:

///

Eventually the compiler can crash. This is because each time a single extra space is added to the global pawndoc string, but that isn't included in the length calculation. The code actually tries to account for this space:

      if (length>0)
        length++;   /* count 1 extra for a separating space */

But as you can see, only if there was some text previously. As this comment is on its own, there isn't and the addition never gets done.

Minimal complete verifiable example (MCVE):

I'm not doing a repro as this only happens when I compile the entire YSI test script with fixes.inc and more - a huge amount of code. Less than that doesn't overflow the buffer enough to affect anything, so the "minimal" repro would be huge. Fortunately, I already found and fixed the issue, this is just here for reference.

@Y-Less
Copy link
Member Author

Y-Less commented Mar 7, 2022

 source/compiler/sc1.c | 41 ++++++++++++++++++++++++-----------------
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c
index a50409e..308da67 100644
--- a/source/compiler/sc1.c
+++ b/source/compiler/sc1.c
@@ -1982,25 +1982,30 @@ void sc_attachdocumentation(symbol *sym)
   /* first check the size */
   length=0;
   for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) {
-    if (length>0)
-      length++;   /* count 1 extra for a separating space */
-    length+=strlen(str);
+    if (str[0]!='\0') {
+      if (length>0)
+        length++;   /* count 1 extra for a separating space */
+      length+=strlen(str);
+    }
   } /* for */
-  if (sym==NULL && sc_documentation!=NULL) {
-    length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
-    assert(length>strlen(sc_documentation));
-  } /* if */
-
   if (length>0) {
-    /* allocate memory for the documentation */
-    if (sym!=NULL && sym->documentation!=NULL)
+    if (sym==NULL && sc_documentation!=NULL) {
+      length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "<p/>" */
+      assert(length > strlen(sc_documentation));
+    } else if (sym!=NULL && sym->documentation!=NULL) {
       length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "<p/>" */
+      assert(length > strlen(sym->documentation));
+    } /* if */
+
+    /* allocate memory for the documentation */
     doc=(char*)malloc((length+1)*sizeof(char));
     if (doc!=NULL) {
       /* initialize string or concatenate */
       if (sym==NULL && sc_documentation!=NULL) {
         strcpy(doc,sc_documentation);
         strcat(doc,"<p/>");
+        free(sc_documentation);
+        sc_documentation=NULL;
       } else if (sym!=NULL && sym->documentation!=NULL) {
         strcpy(doc,sym->documentation);
         strcat(doc,"<p/>");
@@ -2011,9 +2016,11 @@ void sc_attachdocumentation(symbol *sym)
       } /* if */
       /* collect all documentation */
       while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) {
-        if (doc[0]!='\0')
-          strcat(doc," ");
-        strcat(doc,str);
+        if (str[0]!='\0') {
+          if (doc[0]!='\0')
+            strcat(doc," ");
+          strcat(doc,str);
+        }
         delete_docstring(0);
       } /* while */
       if (str!=NULL) {
@@ -2021,12 +2028,12 @@ void sc_attachdocumentation(symbol *sym)
         assert(*str==sDOCSEP);
         delete_docstring(0);
       } /* if */
-      if (sym!=NULL) {
+      if (sym==NULL) {
+        assert(sc_documentation==NULL);
+        sc_documentation=doc;
+      } else {
         assert(sym->documentation==NULL);
         sym->documentation=doc;
-      } else {
-        free(sc_documentation);
-        sc_documentation=doc;
       } /* if */
     } /* if */
   } else {

@Y-Less
Copy link
Member Author

Y-Less commented Mar 7, 2022

That patch fixes this bug and also slightly more unifies the code between function and global pawndoc. Like why was the length modification for global comments done outside the length > 0 check, but local comments was inside? The latter makes more sense for both.

@Y-Less Y-Less closed this as completed Mar 7, 2022
@Y-Less
Copy link
Member Author

Y-Less commented Mar 7, 2022

Leaving this open until I merge.

@Y-Less Y-Less reopened this Mar 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant