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

[CPP][Questions]No effective API to qeury macro used in function parameter declaration #8497

Open
4B5F5F4B opened this issue Mar 19, 2022 · 4 comments
Labels
C++ question Further information is requested

Comments

@4B5F5F4B
Copy link
Contributor

4B5F5F4B commented Mar 19, 2022

Hello all,

Recently I'm interested in writing queries to detect common vulnerability pattern specific for Linux kernel codebase. I find that __user marco is used to indicate parameter is user mode pointer, for example

#define __user          //empty macro body

static int sg_scsi_ioctl(struct request_queue *q, fmode_t mode,
		                       struct scsi_ioctl_command __user *sic)    //sic is a user mode pointer

I want to write a query to get all user mode pointer defined in function parameter declaration, I tried to find avaliable API defined in Marco/MacroAccess/MarcoInovation/Function/Function/FunctionDeclarationEntry/ParameterDeclarationEntry, but I can not find one can be used for my purpose.

I have no way but to query macro used in function parameter declaration by combining MacroAccess and Location, the following code may be work, but it prone to be false positive and ugly:(

class UserParameterDeclarationEntry extends ParameterDeclarationEntry{
    UserParameterDeclarationEntry(){
        exists(MacroAccess m| 
            m.getMacroName() = "__user"
            and m.getFile() = this.getFile()
            and m.getLocation().getEndLine() = this.getLocation().getEndLine()
        )
    }
}

So I hope you guys can be kind enougth to help me find a more effective way to query specific macro in function parameter declaration, thank you:)

@4B5F5F4B 4B5F5F4B added the question Further information is requested label Mar 19, 2022
@tausbn tausbn added the C++ label Mar 21, 2022
@MathiasVP
Copy link
Contributor

Hi @4B5F5F4B,

This is indeed an annoying issue. Finding the macro uses is easy (using MacroInvocation and the related classes), but associating the macro uses with a parameter is very difficult since macros don't fit nicely into the AST (as it's just text being expanded). So there's no good way to do it other than hacking it via locations. This is how I'd do it, I think:

import cpp

pragma[inline]
predicate isBefore(Location l1, Location l2) {
  l1.getFile() = l2.getFile() and
  (
    l1.getEndLine() < l2.getStartLine()
    or
    l1.getEndLine() = l2.getStartLine() and
    l1.getEndColumn() < l2.getStartColumn()
  )
}

predicate isUserParameter(Parameter p) {
  exists(MacroInvocation m |
    p =
      min(Parameter q, TypeMention tm |
        q.getType().stripType() = tm.getMentionedType() and
        m.getMacroName() = "__user" and
        isBefore(tm.getLocation(), m.getLocation()) and
        isBefore(m.getLocation(), q.getLocation())
      |
        q order by q.getIndex()
      )
  )
}

from Parameter p
where isUserParameter(p)
select p, "This parameter is annotated with '__user'."

The idea is that we want, given a parameter Foo* __user x, we want to find a use of the __user macro whose location is in between the type Foo* and the parameter name x.

Let's start by getting the location of the type Foo*: We can't do this in general, but if the type happens to be a user type (like a struct) we can use the TypeMention class to find a use of the type and its location.

Then we want to find the location of the parameter name. Luckily, this is easy. We can just use the location of the Parameter itself.

(Note that the Location class does come with an isBefore predicate, but for some reason that only compares line numbers and not column numbers. So it'll be slightly more robust if you use the predicate is supplied here.)

I hope that helps!

@4B5F5F4B
Copy link
Contributor Author

e predi

Hello @MathiasVP ,

First of all I should thank you for your reply and solution. I can't agree with you more that there is no good way other than hacking via Location, which is quite clearly demonstrated by your code. I'm inspired by your code a lot, thank you. But frankly speaking, too much Location comparison will make it incredibly slow for large codebase like Linux kernel :(
Maybe some speicific optimization is needed in the core engine in CodeQL, I'm looking forward to it:)

Thank you for your help again.

@MathiasVP
Copy link
Contributor

But frankly speaking, too much Location comparison will make it incredibly slow for large codebase like Linux kernel :(

I agree :( This solution is really pushing the limits of what you're meant to do with locations. It's not really the Location comparisons that make it slow - the query is optimized incorrectly (i.e., it's accumulating constraints in a non-optimal order) causing slow evaluation. It might be difficult to speed it up, but I'll see if I can do anything about it.

@ghost
Copy link

ghost commented May 17, 2023

Unfortunately these queries do not match all __user macro indications in function parameters, due to TypeMention only covering user-defined types, per #13214.

For example it doesn't match on copy_from_user and copy_to_user, because these have void * built-in type with __user annotation:

include/linux/uaccess.h

static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
	if (likely(check_copy_size(to, n, false)))
		n = _copy_from_user(to, from, n);
	return n;
}

static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)
{
	if (likely(check_copy_size(from, n, true)))
		n = _copy_to_user(to, from, n);
	return n;
}

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

No branches or pull requests

3 participants