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

A prototype for solve dividing two integers #66

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 91 additions & 2 deletions pyclibrary/c_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,91 @@
__all__ = ['win_defs', 'CParser']


def wrap_int(t):
logger.debug('wrap_int: {} {}'.format(t.dump(), type(t)))
t[0] = "CInt({})".format(eval(t[0]))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rewrite without using eval.

Copy link
Author

@r888800009 r888800009 Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hello @kalvdans,

Thanks for your reply, avoiding using eval is a very good simple suggestion to avoid security issues and everyone should know.

One solution is to write a C integer parser, but I'm not sure it's worth writing. Because my purpose is to bind existing open source c libraries. If some people who want to use untrusted libraries may be worried about this.

Also, I think must write in clean code for better maintenance, but I don't know of any python built-in function that can convert C integer string to a Python integer. If you know a function, I will appreciate if you tell me :)

See also this code https://github.com/r888800009/pyclibrary/blob/f5f8bf80113b9deb247e92705bb4b73529b95912/pyclibrary/c_parser.py#L1736-L1739

The token passed in by wrapper must match the regular expression. If you know how to bypass this regex to achieve python execute code, please tell me and I will be able to confirm that this is indeed a bad approach

Of course, to avoid misuse, we'd better avoiding using eval, but we also need have a good enough solution to make it easy to maintain.

For your concerns, maybe you'll want to look at the existing code in c_parser.py

return eval(expr, *args)

If we care about security issues, maybe we can consider rewriting the eval function of the entire project

Best regards

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not check in depth but could you use ast.literal_eval ?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, int(t[0], 0) should handle 0x prefixes fine, but doesn't work with octal.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we can simply write an octal parser 44daba6 to solve this problem without too much complexity.

new patch a7f7624 uses int(t[0], 0), which can restrict the input of only integer.

return t

class CInt(int):
def __new__(cls, base=10, *args, **kwargs):
return super(CInt, cls).__new__(cls, base, *args, **kwargs)

def __truediv__(self, other):
if isinstance(other, CInt):
is_positive = self >= 0 and other >= 0
return CInt(int(self) // int(other)) if is_positive else -CInt(-int(self) // int(other))
else:
return super(CInt, self).__truediv__(other)

def __rtruediv__(self, other):
if isinstance(other, CInt):
is_positive = self >= 0 and other >= 0
return CInt(int(other) // int(self)) if is_positive else -CInt(-int(other) // int(self))
else:
return super(CInt, self).__rtruediv__(other)

def __mod__(self, other):
if isinstance(other, CInt):
is_positive = self >= 0 and other >= 0
return CInt(int(self) % int(other)) if is_positive else -CInt(-int(self) % int(other))
else:
return super(CInt, self).__mod__(other)

def __rmod__(self, other):
if isinstance(other, CInt):
is_positive = self >= 0 and other >= 0
return CInt(int(other) % int(self)) if is_positive else -CInt(-int(other) % int(self))
else:
return super(CInt, self).__rmod__(other)

def __sub__(self, other):
if isinstance(other, CInt):
return CInt(int(self) - int(other))
else:
return super(CInt, self).__sub__(other)

def __rsub__(self, other):
if isinstance(other, CInt):
return CInt(int(other) - int(self))
else:
return super(CInt, self).__rsub__(other)

def __add__(self, other):
if isinstance(other, CInt):
return CInt(int(self) + int(other))
else:
return super(CInt, self).__add__(other)

def __radd__(self, other):
if isinstance(other, CInt):
return CInt(int(other) + int(self))
else:
return super(CInt, self).__radd__(other)

def __mul__(self, other):
if isinstance(other, CInt):
return CInt(int(self) * int(other))
else:
return super(CInt, self).__mul__(other)

def __rmul__(self, other):
if isinstance(other, CInt):
return CInt(int(other) * int(self))
else:
return super(CInt, self).__rmul__(other)

def __str__(self):
return "CInt({})".format(super(CInt, self).__str__())

def __repr__(self):
return "CInt({})".format(super(CInt, self).__repr__())

def __neg__(self):
return CInt(-int(self))

def __pos__(self):
return CInt(+int(self))

class Type(tuple):
"""
Representation of a C type. CParser uses this class to store the parsed
Expand Down Expand Up @@ -1508,7 +1593,7 @@ def eval(self, expr, *args):
expr = expr.strip()
cast = (lparen + self.type_spec + self.abstract_declarator +
rparen).suppress()
expr = (quotedString | number | cast).transformString(expr)
expr = (quotedString | number_in_expr | cast).transformString(expr)
if expr == '':
return None
return eval(expr, *args)
Expand Down Expand Up @@ -1651,10 +1736,14 @@ def print_parse_results(pr, depth=0, name=''):
hexint = Regex(r'[+-]?\s*0[xX][{}]+[UL]*'.format(hexnums)).setParseAction(int_strip)
decint = Regex(r'[+-]?\s*[0-9]+[UL]*').setParseAction(int_strip)
integer = (hexint | decint)
integer.setParseAction(wrap_int)
# in eval expr would not match identifier, it would match a number cause error
integer_in_expr = (hexint | decint)
# The floating regex is ugly but it is because we do not want to match
# integer to it.
floating = Regex(r'[+-]?\s*((((\d(\.\d*)?)|(\.\d+))[eE][+-]?\d+)|((\d\.\d*)|(\.\d+)))')
number = (floating | integer)
number = (floating |integer)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coding style: use spaces around binary operators

number_in_expr = (floating |integer_in_expr)

# Miscelaneous
bi_operator = oneOf("+ - / * | & || && ! ~ ^ % == != > < >= <= -> . :: << >> = ? :")
Expand Down
12 changes: 12 additions & 0 deletions tests/headers/variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ float x1 = (5 + 3 * 0x1) / 8.0;
// Test type casting handling.
int x2 = (typeCast)0x544 <<16;

// Test int div 9 / 2 should be 4
float x3 = 9.0 / 2.0;
int x4 = 9 / 2;
float x5 = 9 / 2;
int x6_1 = 9;
int x6_2 = 2;
float x6 = x6_1 / x6_2;
float x7 = x6_1 / 2.0;
float x8 = (9 / 2) * 2 + 9 % 2;
int x9 = -9 / 2; // -4
int x10 = -9 % 2; // -1

// Test array
float array[2] = {0x1, 3.1415e6};
static const int * const (**intJunk[4]);
Expand Down
18 changes: 18 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,24 @@ def test_variables(self):
assert ('x2' in variables and
variables['x2'] == (88342528, Type('int')))

# Test int div 9 / 2 should be 4
assert ('x3' in variables and
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert ('x3' in variables and
assert (

the indexing will anyway if the key does not exist in the dict. No need to test it separately.

variables['x3'] == (4.5, Type('float')))
assert ('x4' in variables and
variables['x4'] == (4, Type('int')))
assert ('x5' in variables and
variables['x5'] == (4., Type('float')))
assert ('x6' in variables and
variables['x6'] == (4., Type('float')))
assert ('x7' in variables and
variables['x7'] == (4.5, Type('float')))
assert ('x8' in variables and
variables['x8'] == (9., Type('float')))
assert ('x9' in variables and
variables['x9'] == (-4, Type('int')))
assert ('x10' in variables and
variables['x10'] == (-1, Type('int')))

# Test array handling
assert ('array' in variables and
variables['array'] == ([1, 3141500.0], Type('float', [2])))
Expand Down