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

How to check CWE-404 when throw exception #17319

Open
ysuLihua opened this issue Aug 28, 2024 · 5 comments
Open

How to check CWE-404 when throw exception #17319

ysuLihua opened this issue Aug 28, 2024 · 5 comments
Labels
awaiting-response The CodeQL team is awaiting further input or clarification from the original reporter of this issue. question Further information is requested Stale

Comments

@ysuLihua
Copy link

Description of the issue

This is test code:
test_throw.h

#define _O_RDONLY      0x0000  // open for reading only
namespace OCKIO {
namespace MSG {
void Throw(const char* func, const char* file)
{
    throw("error");
}
}
};

test_throw.cpp

#include "test_throw.h"
#include <fcntl.h>
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 

namespace OCKIO {
namespace SHORE {
namespace PROCESSOR {
class TransPosix {
private:
    void ReadJewel();
};
void TransPosix::ReadJewel(){
    auto fdData = open("a.txt", 0x0000);
    if (fdData < 0) {
        MSG::Throw("error", "file opening failed");
    }
    if (lseek(fdData, 5, SEEK_SET) > 20){
        // no close
        **MSG::Throw("error", "file opening failed");**
    }
    close(fdData);
}
}
}
};

How can I check fdData is closed, before throw?

@ysuLihua ysuLihua added the question Further information is requested label Aug 28, 2024
@ysuLihua ysuLihua changed the title How to check CWE-404 where throw exception How to check CWE-404 whethrow exception Aug 28, 2024
@ysuLihua ysuLihua changed the title How to check CWE-404 whethrow exception How to check CWE-404 when throw exception Aug 28, 2024
@rvermeulen
Copy link
Contributor

rvermeulen commented Aug 28, 2024

@ysuLihua thanks for your question.

You have a few options to consider:

  • Our Dominance allows you to determine if a node in the control flow graph is always preceded or followed by another node. This can for example be used to determine if MSG:Throw invocation that is dominated by an open call is not dominated by a close call. In other words, there is a path from open to MSG:Throw that doesn't contain a close. This doesn't yet consider the parameters to open and close so it might yield FN if those are different.
  • You can write your own recursive predicate to determine a path between open and MSG::Throw that doesn't go through a close. The module GVN may help with determining that the arguments have the same value.

These options, however, only considers intra-procedural analysis. You can extend those options to inter-procedural, but you would need to handle the calls yourself.

The following query may provide a good example cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql.
It implements the second option using the reachesTermination predicate.

Let me know if this helps.

@rvermeulen rvermeulen added the awaiting-response The CodeQL team is awaiting further input or clarification from the original reporter of this issue. label Aug 28, 2024
@ysuLihua
Copy link
Author

Thank you. I'll learn about this.

@ysuLihua ysuLihua reopened this Sep 3, 2024
@ysuLihua
Copy link
Author

ysuLihua commented Sep 3, 2024

@rvermeulen thanks for your suggestions!
Now I define:
Open file as FileStreamOpenExpr, and Throw terminate as TerminationCallExpr. I try to use TaintTracking to check it, but the check result is none. Can TaintTracking be used to detect this scenario?

Here's a code example:

class Config extends TaintTracking::Configuration {
  Config() { this = "Config: this name doesn't matter" }

  override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof FileStreamOpenExpr }
  
  override predicate isSink(DataFlow::Node sink) {
    sink.asExpr() instanceof TerminationCallExpr
  }
}

from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "open file stream flows to exception terminate."

@rvermeulen
Copy link
Contributor

Hi @ysuLihua,

Unfortunately you cannot model this with data flow, because the value returned from open doesn't flow into the throw. This has to be checked with control flow.

The cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql uses control flow by finding a path using the recursive predicate reachesTermination.

The predicate can be adjusted in your case by starting at the open call and finding a path to throw that doesn't encounter a close call.
Have a look at the recursive case in that predicate to see how you can stop it when it encounters a stop condition (in this case a close call).

Data flow can be used to determine if the value from open reaches an encountered close to ensure the same file descriptor is closed (to keep things simple you can skip this data flow step and implement it if there are too many false positives which I don't expect).

Copy link
Contributor

This issue is stale because it has been open 14 days with no activity. Comment or remove the Stale label in order to avoid having this issue closed in 7 days.

@github-actions github-actions bot added the Stale label Sep 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting-response The CodeQL team is awaiting further input or clarification from the original reporter of this issue. question Further information is requested Stale
Projects
None yet
Development

No branches or pull requests

2 participants