1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00

pidl: Change PIDL to correctly use and validate python integer types

In particular, it is critical that we use unsigned integers of
sufficient size in python for unsigned C integers, and it is
critical that we check for overflow at both the python and C
level.

Otherwise, we may both represent and sort these incorrectly,
in particular when sorting attributeID values from DRSUAPI
which are represented as an signed enum in C and a uint32_t in IDL,
but which often has the high bit set (in schema extensions).

BUG: https://bugzilla.samba.org/show_bug.cgi?id=11429

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
Andrew Bartlett 2015-07-27 10:57:43 +12:00 committed by Stefan Metzmacher
parent 3faa7dc5a7
commit 5206ccd002

View File

@ -10,7 +10,7 @@ use Exporter;
use strict;
use Parse::Pidl qw(warning fatal error);
use Parse::Pidl::Typelist qw(hasType resolveType getType mapTypeName expandAlias);
use Parse::Pidl::Typelist qw(hasType resolveType getType mapTypeName expandAlias bitmap_type_fn enum_type_fn);
use Parse::Pidl::Util qw(has_property ParseExpr unmake_str);
use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred ContainsPipe is_charset_array);
use Parse::Pidl::CUtil qw(get_value_of get_pointer_to);
@ -953,14 +953,58 @@ sub ConvertObjectFromPythonData($$$$$$;$)
$actual_ctype = $actual_ctype->{DATA};
}
if ($actual_ctype->{TYPE} eq "ENUM" or $actual_ctype->{TYPE} eq "BITMAP") {
# We need to cover ENUMs, BITMAPS and SCALAR values here, as
# all could otherwise be assigned invalid integer values
my $ctype_alias = "";
my $uint_max = "";
if ($actual_ctype->{TYPE} eq "ENUM") {
# Importantly, ENUM values are unsigned in pidl, and
# typically map to uint32
$ctype_alias = enum_type_fn($actual_ctype);
} elsif ($actual_ctype->{TYPE} eq "BITMAP") {
$ctype_alias = bitmap_type_fn($actual_ctype);
} elsif ($actual_ctype->{TYPE} eq "SCALAR") {
$ctype_alias = expandAlias($actual_ctype->{NAME});
}
# This is the unsigned Python Integer -> C integer validation
# case. The signed case is below.
if ($ctype_alias =~ /^(uint[0-9]*|hyper|udlong|udlongr
|NTTIME_hyper|NTTIME|NTTIME_1sec
|uid_t|gid_t)$/x) {
$self->pidl("{");
$self->indent;
$self->pidl("const unsigned long long uint_max = (sizeof($target) == 8) ? UINT64_MAX : (unsigned long long)((1ULL << (sizeof($target) * 8)) - 1);");
$self->pidl("if (PyLong_Check($cvar)) {");
$self->indent;
$self->pidl("$target = PyLong_AsLongLong($cvar);");
$self->pidl("unsigned long long test_var;");
$self->pidl("test_var = PyLong_AsUnsignedLongLong($cvar);");
$self->pidl("if (PyErr_Occurred() != NULL) {");
$self->indent;
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->pidl("if (test_var > uint_max) {");
$self->indent;
$self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s or %s within range 0 - %llu, got %llu\",\\");
$self->pidl(" PyInt_Type.tp_name, PyLong_Type.tp_name, uint_max, test_var);");
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->pidl("$target = test_var;");
$self->deindent;
$self->pidl("} else if (PyInt_Check($cvar)) {");
$self->indent;
$self->pidl("$target = PyInt_AsLong($cvar);");
$self->pidl("long test_var;");
$self->pidl("test_var = PyInt_AsLong($cvar);");
$self->pidl("if (test_var < 0 || test_var > uint_max) {");
$self->indent;
$self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s or %s within range 0 - %llu, got %ld\",\\");
$self->pidl(" PyInt_Type.tp_name, PyLong_Type.tp_name, uint_max, test_var);");
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->pidl("$target = test_var;");
$self->deindent;
$self->pidl("} else {");
$self->indent;
@ -969,34 +1013,61 @@ sub ConvertObjectFromPythonData($$$$$$;$)
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->deindent;
$self->pidl("}");
return;
}
if ($actual_ctype->{TYPE} eq "SCALAR" ) {
if (expandAlias($actual_ctype->{NAME}) =~ /^(u?int64|hyper|dlong|udlong|udlongr
|NTTIME_hyper|NTTIME|NTTIME_1sec
|uid_t|gid_t)$/x) {
$self->pidl("if (PyLong_Check($cvar)) {");
$self->indent;
$self->pidl("$target = PyLong_AsLongLong($cvar);");
$self->deindent;
$self->pidl("} else if (PyInt_Check($cvar)) {");
$self->indent;
$self->pidl("$target = PyInt_AsLong($cvar);");
$self->deindent;
$self->pidl("} else {");
$self->indent;
$self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s or %s\",\\");
$self->pidl(" PyInt_Type.tp_name, PyLong_Type.tp_name);");
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
return;
}
if (expandAlias($actual_ctype->{NAME}) =~ /^(char|u?int[0-9]*|time_t)$/) {
$self->pidl("PY_CHECK_TYPE(&PyInt_Type, $cvar, $fail);");
$self->pidl("$target = PyInt_AsLong($cvar);");
return;
}
# Confirm the signed python integer fits in the C type
# correctly. It is subtly different from the unsigned case
# above, so while it looks like a duplicate, it is not
# actually a duplicate.
if ($ctype_alias =~ /^(dlong|char|int[0-9]*|time_t)$/x) {
$self->pidl("{");
$self->indent;
$self->pidl("const long long int_max = (long long)((1ULL << (sizeof($target) * 8 - 1)) - 1);");
$self->pidl("const long long int_min = -int_max - 1;");
$self->pidl("if (PyLong_Check($cvar)) {");
$self->indent;
$self->pidl("long long test_var;");
$self->pidl("test_var = PyLong_AsLongLong($cvar);");
$self->pidl("if (PyErr_Occurred() != NULL) {");
$self->indent;
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->pidl("if (test_var < int_min || test_var > int_max) {");
$self->indent;
$self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s or %s within range %lld - %lld, got %lld\",\\");
$self->pidl(" PyInt_Type.tp_name, PyLong_Type.tp_name, int_min, int_max, test_var);");
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->pidl("$target = test_var;");
$self->deindent;
$self->pidl("} else if (PyInt_Check($cvar)) {");
$self->indent;
$self->pidl("long test_var;");
$self->pidl("test_var = PyInt_AsLong($cvar);");
$self->pidl("if (test_var < int_min || test_var > int_max) {");
$self->indent;
$self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s or %s within range %lld - %lld, got %ld\",\\");
$self->pidl(" PyInt_Type.tp_name, PyLong_Type.tp_name, int_min, int_max, test_var);");
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->pidl("$target = test_var;");
$self->deindent;
$self->pidl("} else {");
$self->indent;
$self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s or %s\",\\");
$self->pidl(" PyInt_Type.tp_name, PyLong_Type.tp_name);");
$self->pidl($fail);
$self->deindent;
$self->pidl("}");
$self->deindent;
$self->pidl("}");
return;
}
if ($actual_ctype->{TYPE} eq "STRUCT" or $actual_ctype->{TYPE} eq "INTERFACE") {
@ -1202,17 +1273,29 @@ sub ConvertScalarToPython($$$)
$ctypename = expandAlias($ctypename);
if ($ctypename =~ /^(int64|dlong)$/) {
return "PyLong_FromLongLong($cvar)";
return "($cvar > LONG_MAX || $cvar < LONG_MIN) ? PyLong_FromLongLong($cvar) : PyInt_FromLong($cvar)";
}
if ($ctypename =~ /^(uint64|hyper|udlong|udlongr|NTTIME_hyper|NTTIME|NTTIME_1sec|uid_t|gid_t)$/) {
return "PyLong_FromUnsignedLongLong($cvar)";
if ($ctypename =~ /^(uint64|hyper|NTTIME_hyper|NTTIME|NTTIME_1sec|udlong|udlongr|uid_t|gid_t)$/) {
return "$cvar > LONG_MAX ? PyLong_FromUnsignedLongLong($cvar) : PyInt_FromLong($cvar)";
}
if ($ctypename =~ /^(char|u?int[0-9]*|time_t)$/) {
if ($ctypename =~ /^(char|int|int8|int16|int32|time_t)$/) {
return "PyInt_FromLong($cvar)";
}
# Needed to ensure unsigned values in a 32 or 16 bit enum is
# cast correctly to a uint32_t, not sign extended to a a
# possibly 64 bit unsigned long. (enums are signed in C,
# unsigned in NDR)
if ($ctypename =~ /^(uint32|uint3264)$/) {
return "(uint32_t)$cvar > LONG_MAX ? PyLong_FromUnsignedLong((uint32_t)$cvar) : PyInt_FromLong((uint32_t)$cvar)";
}
if ($ctypename =~ /^(uint|uint8|uint16|uint1632)$/) {
return "PyInt_FromLong((uint16_t)$cvar)";
}
if ($ctypename eq "DATA_BLOB") {
return "PyString_FromStringAndSize((char *)($cvar).data, ($cvar).length)";
}
@ -1487,7 +1570,7 @@ sub Parse($$$$$)
my $py_obj;
my ($ctype, $cvar) = @{$h->{'val'}};
if ($cvar =~ /^[0-9]+$/ or $cvar =~ /^0x[0-9a-fA-F]+$/) {
$py_obj = "PyInt_FromLong($cvar)";
$py_obj = "$cvar > LONG_MAX ? PyLong_FromUnsignedLongLong($cvar) : PyInt_FromLong($cvar)";
} elsif ($cvar =~ /^".*"$/) {
$py_obj = "PyString_FromString($cvar)";
} else {