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

Find a double free #12455

Open
puya-pakshad opened this issue Mar 8, 2023 · 5 comments
Open

Find a double free #12455

puya-pakshad opened this issue Mar 8, 2023 · 5 comments
Labels
question Further information is requested

Comments

@puya-pakshad
Copy link

puya-pakshad commented Mar 8, 2023

Description of the issue

I possess a source code and my goal is to identify any instances of double free vulnerabilities. Technically, the double free vulnerabilities within this source code are associated with three sets of free(): lines 109, 110, and 111, as well as their corresponding pairs at lines 128, 129, and 130. I have examined numerous .ql queries that have been created by the authors of codeql, but none of them have been effective in detecting double free vulnerabilities.
Would anyone be able to assist me with this matter, please?

`

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  char *type;
  int size;
  char *data;
} chunk_t;

chunk_t* find_BOOM(FILE *fp) {
  char signature[5];
  signature[4] = 0; //NULL terminated string
  chunk_t* chunkBOOM = NULL;

  // check the signature
  if (fread(signature, 4, 1, fp)) {
    if (strcmp(signature, "ABCD")) {
      printf("Error! Invalid file signature\n");
      goto quit;
    }
  } else {
    printf("Error! Invalid file\n");
    goto quit;
  }

  // read the content until the end of the file
  // or until a BOOM chunk is found
  while(!feof(fp)) {
    char ctype[5];
    ctype[4] = 0; //NULL terminated string
    unsigned int csize;
    char *cdata = NULL;

    // read chunk type
    if (fread(ctype, 4, 1, fp)) {
      if (fread(&csize, 4, 1, fp)) {

        cdata = (char *) malloc(csize);
        if (cdata == NULL) {
          printf("Error! malloc fails\n");
          goto quit;
        }
        
        if (fread(cdata, csize, 1, fp)) {
          if (!strcmp(ctype, "BOOM") && csize == 8) {
            chunkBOOM = (chunk_t *) malloc(sizeof(chunk_t));
            if (chunkBOOM == NULL) {
              printf("Error! malloc fails\n");
              goto quit;
            }

            chunkBOOM->type = (char *) malloc(5);
            if (chunkBOOM->type == NULL) {
              printf("Error! malloc fails\n");
              goto quit;
            }
            memcpy(chunkBOOM->type, ctype, 5);

            chunkBOOM->size = csize;

            chunkBOOM->data = (char *) malloc(csize);
            if (chunkBOOM->data == NULL) {
              printf("Error! malloc fails\n");
              goto quit;
            }
            memcpy(chunkBOOM->data, cdata, csize);
            
            if (cdata != NULL) free(cdata);
            goto quit;
          }
        } else {
          printf("Error while reading chunk data\n");
          goto quit;
        }
      } else {
        printf("Error while reading chunk size\n");
        goto quit;
      }
    } else {
      if (feof(fp)) {
        printf("End of file\n");
        break;
      }

      printf("Error while reading chunk type\n");
      goto quit;
    }
    
    // free cdata before reading the next chunk
    if (cdata != NULL) free(cdata);
  }

quit:
  return chunkBOOM;
}

void process_BOOM(chunk_t *chunkBOOM) {
  unsigned int x, y, z;

  if (chunkBOOM == NULL) return;

  memcpy(&x, chunkBOOM->data, 4);
  memcpy(&y, &(chunkBOOM->data[4]), 4);
  
  z = x + y;
  //AIF_RANGE(0,z,284,285);
  if ((z > 283) && (z < 286)) {
    free(chunkBOOM->type);
    free(chunkBOOM->data);
    free(chunkBOOM);
  }
}

int main(int argc, char** argv) {
  FILE *fp;

  if ((fp = fopen(argv[1],"rb")) == NULL){
    printf("Error! opening file\n");
    exit(1);
  }

  chunk_t *chunkBOOM = find_BOOM(fp);
  process_BOOM(chunkBOOM);

  if (chunkBOOM != NULL) {
    // free chunkBOOM
    free(chunkBOOM->type);
    free(chunkBOOM->data);
    free(chunkBOOM);
  }

  // close file 
  fclose(fp); 
  
  return 0;
}
@puya-pakshad puya-pakshad added the question Further information is requested label Mar 8, 2023
@tausbn
Copy link
Contributor

tausbn commented Mar 9, 2023

Thank you for your question!

While there aren't any queries in the default suite that have to do with detecting "double free" errors, there is an experimental query that does this:

https://github.com/github/codeql/blob/main/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql

Being experimental, there are no guarantees that it actually works on your code, but I guess you could give it a shot and see if it fits your use-case. 🙂

@puya-pakshad
Copy link
Author

puya-pakshad commented Mar 10, 2023

Thank you for your helpful response to my inquiry. I attempted to utilize CWE-415/DoubleFree.ql to address my problem, but unfortunately it was not effective. However, I have since written a codeql query that has partially achieved the desired outcome, though it still requires some changes. Here is my codeql for your consideration:

predicate isFreeExpr(ControlFlowNode e, Variable v) {
  exists(VariableAccess va | va.getTarget() = v |
    exists(FunctionCall fc | fc = e |
      fc.getTarget().hasGlobalOrStdName("free") and
      (va = fc.getArgument(0))
    ))  
}

from ControlFlowNode e1 , ControlFlowNode e2 , Variable v
where (isFreeExpr(e1, v) and isFreeExpr(e2, v))  
and (e1 != e2) and (e1.getLocation().getStartLine() < e2.getLocation().getStartLine()) 
select e1.getLocation().getStartLine() , e1 , v, e2.getLocation().getStartLine() , e2 , v

The current search can discover connections among lines (69, 91), (109, 128), and (110, 129), but there is still a need to identify connections for lines (111, 130) that have not yet been identified. Can you assist me in resolving this problem? @tausbn @bgianfo @krukow @twpayne

Thank you very much!

@twpayne
Copy link
Contributor

twpayne commented Mar 10, 2023

Please do not spam me with mentions. If it happens I again I will report you to GitHub for abuse.

@tausbn
Copy link
Contributor

tausbn commented Mar 10, 2023

I think the issue with your query is that the occurrences of chunkBOOM on lines 111 and 130 are different Variables in the eyes of the CodeQL analysis, because they are in different functions (though in that case, I'm not entirely sure why it would work for 109 and 128).

This goes beyond my knowledge of how the C analysis works, so I'll loop in someone from the C team to take a look at it.

@geoffw0
Copy link
Contributor

geoffw0 commented Mar 14, 2023

Hi @puya-pakshad, we have a couple of suggestions:

  1. You could extend your query to find the result for lines (111, 130) by adding a predicate along the lines of:
predicate mayFreedParameter(Function f, int paramIndex) {
  exists(FunctionCall fc |
    fc.getEnclosingFunction() = f and
    isFreeExpr(fc, f.getParameter(paramIndex))
  )
}

then extend isFreeExpr with:

      or
      exists(int argIndex |
        mayFreedParameter(fc.getTarget(), argIndex) and
        fc.getArgument(argIndex) = va
      )

This ought to catch the specific case you're interested in and many like it, but it won't cover everything. I also think you're going to start seeing false positive results if you aren't already (restricting e1 and e2 to be in the same function may help a little).

  1. Alternatively you could use the semmle.code.cpp.dataflow.DataFlow library, or perhaps the newer semmle.code.cpp.ir.dataflow.DataFlow library, to track the flow of values interprocedurally. The latter will soon support 'use-use' flow that will make it much easier to write queries like this where you're trying to connect two uses of the same value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants