summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm107
-rw-r--r--source4/pidl/lib/Parse/Pidl/Util.pm19
-rwxr-xr-xsource4/pidl/tests/samba-ndr.pl135
3 files changed, 219 insertions, 42 deletions
diff --git a/source4/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm b/source4/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm
index debdc8e308..49f9e051a8 100644
--- a/source4/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm
+++ b/source4/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm
@@ -10,12 +10,14 @@ package Parse::Pidl::Samba4::NDR::Parser;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(is_charset_array);
+@EXPORT_OK = qw(check_null_pointer);
use strict;
use Parse::Pidl::Typelist qw(hasType getType mapType);
-use Parse::Pidl::Util qw(has_property ParseExpr print_uuid);
+use Parse::Pidl::Util qw(has_property ParseExpr ParseExprExt print_uuid);
use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred);
use Parse::Pidl::Samba4 qw(is_intree choose_header);
+use Parse::Pidl qw(warning);
use vars qw($VERSION);
$VERSION = '0.01';
@@ -165,29 +167,6 @@ sub deindent()
}
#####################################################################
-# check that a variable we get from ParseExpr isn't a null pointer
-sub check_null_pointer($)
-{
- my $size = shift;
- if ($size =~ /^\*/) {
- my $size2 = substr($size, 1);
- pidl "if ($size2 == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;";
- }
-}
-
-#####################################################################
-# check that a variable we get from ParseExpr isn't a null pointer,
-# putting the check at the end of the structure/function
-sub check_null_pointer_deferred($)
-{
- my $size = shift;
- if ($size =~ /^\*/) {
- my $size2 = substr($size, 1);
- defer "if ($size2 == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;";
- }
-}
-
-#####################################################################
# declare a function public or static, depending on its attributes
sub fn_declare($$$)
{
@@ -325,6 +304,61 @@ sub ParseArrayPushHeader($$$$$)
return $length;
}
+sub check_null_pointer($$$)
+{
+ my ($element, $env, $print_fn) = @_;
+
+ return sub ($) {
+ my $expandedvar = shift;
+ my $check = 0;
+
+ # Figure out the number of pointers in $ptr
+ $expandedvar =~ s/^(\**)//;
+ my $ptr = $1;
+
+ my $var = undef;
+ foreach (keys %$env) {
+ if ($env->{$_} eq $expandedvar) {
+ $var = $_;
+ last;
+ }
+ }
+
+ if (defined($var)) {
+ my $e;
+ # lookup ptr in $e
+ foreach (@{$element->{PARENT}->{ELEMENTS}}) {
+ if ($_->{NAME} eq $var) {
+ $e = $_;
+ last;
+ }
+ }
+
+ $e or die("Environment doesn't match siblings");
+
+ # See if pointer at pointer level $level
+ # needs to be checked.
+ foreach my $l (@{$e->{LEVELS}}) {
+ if ($l->{TYPE} eq "POINTER" and
+ $l->{POINTER_INDEX} == length($ptr)) {
+ # No need to check ref pointers
+ $check = ($l->{POINTER_TYPE} ne "ref");
+ last;
+ }
+
+ if ($l->{TYPE} eq "DATA") {
+ warning($element, "too much dereferences for `$var'");
+ }
+ }
+ } else {
+ warning($element, "unknown dereferenced expression `$expandedvar'");
+ $check = 1;
+ }
+
+ $print_fn->("if ($ptr$expandedvar == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;") if $check;
+ }
+}
+
#####################################################################
# parse an array - pull side
sub ParseArrayPullHeader($$$$$)
@@ -339,21 +373,19 @@ sub ParseArrayPullHeader($$$$$)
} elsif ($l->{IS_ZERO_TERMINATED}) { # Noheader arrays
$length = $size = "ndr_get_string_size($ndr, sizeof(*$var_name))";
} else {
- $length = $size = ParseExpr($l->{SIZE_IS}, $env, $e);
+ $length = $size = ParseExprExt($l->{SIZE_IS}, $env, $e,
+ check_null_pointer($e, $env, \&pidl));
}
if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) {
pidl "NDR_CHECK(ndr_pull_array_size(ndr, " . get_pointer_to($var_name) . "));";
}
-
if ($l->{IS_VARYING}) {
pidl "NDR_CHECK(ndr_pull_array_length($ndr, " . get_pointer_to($var_name) . "));";
$length = "ndr_get_array_length($ndr, " . get_pointer_to($var_name) .")";
}
- check_null_pointer($length);
-
if ($length ne $size) {
pidl "if ($length > $size) {";
indent;
@@ -363,20 +395,18 @@ sub ParseArrayPullHeader($$$$$)
}
if ($l->{IS_CONFORMANT} and not $l->{IS_ZERO_TERMINATED}) {
- my $size = ParseExpr($l->{SIZE_IS}, $env, $e);
defer "if ($var_name) {";
defer_indent;
- check_null_pointer_deferred($size);
+ my $size = ParseExprExt($l->{SIZE_IS}, $env, $e, check_null_pointer($e, $env, \&defer));
defer "NDR_CHECK(ndr_check_array_size(ndr, (void*)" . get_pointer_to($var_name) . ", $size));";
defer_deindent;
defer "}";
}
if ($l->{IS_VARYING} and not $l->{IS_ZERO_TERMINATED}) {
- my $length = ParseExpr($l->{LENGTH_IS}, $env, $e);
defer "if ($var_name) {";
defer_indent;
- check_null_pointer_deferred($length);
+ my $length = ParseExprExt($l->{LENGTH_IS}, $env, $e, check_null_pointer($e, $env, \&defer));
defer "NDR_CHECK(ndr_check_array_length(ndr, (void*)" . get_pointer_to($var_name) . ", $length));";
defer_deindent;
defer "}"
@@ -661,7 +691,7 @@ sub ParsePtrPush($$$)
my ($e,$l,$var_name) = @_;
if ($l->{POINTER_TYPE} eq "ref") {
- check_null_pointer(get_value_of($var_name));
+ pidl "if ($var_name == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;";
if ($l->{LEVEL} eq "EMBEDDED") {
pidl "NDR_CHECK(ndr_push_ref_ptr(ndr));";
}
@@ -774,9 +804,7 @@ sub ParseElementPrint($$$)
sub ParseSwitchPull($$$$$$)
{
my($e,$l,$ndr,$var_name,$ndr_flags,$env) = @_;
- my $switch_var = ParseExpr($l->{SWITCH_IS}, $env, $e);
-
- check_null_pointer($switch_var);
+ my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e, check_null_pointer($e, $env, \&pidl));
$var_name = get_pointer_to($var_name);
pidl "NDR_CHECK(ndr_pull_set_switch_value($ndr, $var_name, $switch_var));";
@@ -787,9 +815,8 @@ sub ParseSwitchPull($$$$$$)
sub ParseSwitchPush($$$$$$)
{
my($e,$l,$ndr,$var_name,$ndr_flags,$env) = @_;
- my $switch_var = ParseExpr($l->{SWITCH_IS}, $env, $e);
+ my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e, check_null_pointer($e, $env, \&pidl));
- check_null_pointer($switch_var);
$var_name = get_pointer_to($var_name);
pidl "NDR_CHECK(ndr_push_set_switch_value($ndr, $var_name, $switch_var));";
}
@@ -2014,7 +2041,6 @@ sub AllocateArrayLevel($$$$$)
my $var = ParseExpr($e->{NAME}, $env, $e);
- check_null_pointer($size);
my $pl = GetPrevLevel($e, $l);
if (defined($pl) and
$pl->{TYPE} eq "POINTER" and
@@ -2093,8 +2119,7 @@ sub ParseFunctionPull($)
and $e->{LEVELS}[1]->{IS_ZERO_TERMINATED});
if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") {
- my $size = ParseExpr($e->{LEVELS}[1]->{SIZE_IS}, $env, $e);
- check_null_pointer($size);
+ my $size = ParseExprExt($e->{LEVELS}[1]->{SIZE_IS}, $env, $e, check_null_pointer($e, $env, \&pidl));
pidl "NDR_PULL_ALLOC_N(ndr, r->out.$e->{NAME}, $size);";
diff --git a/source4/pidl/lib/Parse/Pidl/Util.pm b/source4/pidl/lib/Parse/Pidl/Util.pm
index 35338492dd..35e25286f5 100644
--- a/source4/pidl/lib/Parse/Pidl/Util.pm
+++ b/source4/pidl/lib/Parse/Pidl/Util.pm
@@ -6,7 +6,7 @@ package Parse::Pidl::Util;
require Exporter;
@ISA = qw(Exporter);
-@EXPORT = qw(has_property property_matches ParseExpr is_constant make_str print_uuid MyDumper);
+@EXPORT = qw(has_property property_matches ParseExpr ParseExprExt is_constant make_str print_uuid MyDumper);
use vars qw($VERSION);
$VERSION = '0.01';
@@ -115,4 +115,21 @@ sub ParseExpr($$$)
undef);
}
+sub ParseExprExt($$$$)
+{
+ my($expr, $varlist, $e, $deref) = @_;
+
+ die("Undefined value in ParseExpr") if not defined($expr);
+
+ my $x = new Parse::Pidl::Expr();
+
+ return $x->Run($expr, sub { my $x = shift; error($e, $x); },
+ # Lookup fn
+ sub { my $x = shift;
+ return($varlist->{$x}) if (defined($varlist->{$x}));
+ return $x;
+ },
+ $deref);
+}
+
1;
diff --git a/source4/pidl/tests/samba-ndr.pl b/source4/pidl/tests/samba-ndr.pl
new file mode 100755
index 0000000000..487f203f41
--- /dev/null
+++ b/source4/pidl/tests/samba-ndr.pl
@@ -0,0 +1,135 @@
+#!/usr/bin/perl
+# (C) 2007 Jelmer Vernooij <jelmer@samba.org>
+# Published under the GNU General Public License
+use strict;
+use warnings;
+
+use Test::More tests => 10;
+use FindBin qw($RealBin);
+use lib "$RealBin";
+use Util;
+use Parse::Pidl::Util qw(MyDumper);
+use Parse::Pidl::Samba4::NDR::Parser qw(check_null_pointer);
+
+my $output;
+sub print_fn($) { my $x = shift; $output.=$x; }
+
+# Test case 1: Simple unique pointer dereference
+
+$output = "";
+my $fn = check_null_pointer({
+ PARENT => {
+ ELEMENTS => [
+ {
+ NAME => "bla",
+ LEVELS => [
+ { TYPE => "POINTER",
+ POINTER_INDEX => 0,
+ POINTER_TYPE => "unique" },
+ { TYPE => "DATA" }
+ ],
+ },
+ ]
+ }
+}, { bla => "r->in.bla" }, \&print_fn);
+
+
+test_warnings("", sub { $fn->("r->in.bla"); });
+
+is($output, "if (r->in.bla == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;");
+
+# Test case 2: Simple ref pointer dereference
+
+$output = "";
+$fn = check_null_pointer({
+ PARENT => {
+ ELEMENTS => [
+ {
+ NAME => "bla",
+ LEVELS => [
+ { TYPE => "POINTER",
+ POINTER_INDEX => 0,
+ POINTER_TYPE => "ref" },
+ { TYPE => "DATA" }
+ ],
+ },
+ ]
+ }
+}, { bla => "r->in.bla" }, \&print_fn);
+
+test_warnings("", sub { $fn->("r->in.bla"); });
+
+is($output, "");
+
+# Test case 3: Illegal dereference
+
+$output = "";
+$fn = check_null_pointer({
+ FILE => "nofile",
+ LINE => 1,
+ PARENT => {
+ ELEMENTS => [
+ {
+ NAME => "bla",
+ LEVELS => [
+ { TYPE => "DATA" }
+ ],
+ },
+ ]
+ }
+}, { bla => "r->in.bla" }, \&print_fn);
+
+test_warnings("nofile:1: too much dereferences for `bla'\n",
+ sub { $fn->("r->in.bla"); });
+
+is($output, "");
+
+# Test case 4: Double pointer dereference
+
+$output = "";
+$fn = check_null_pointer({
+ PARENT => {
+ ELEMENTS => [
+ {
+ NAME => "bla",
+ LEVELS => [
+ { TYPE => "POINTER",
+ POINTER_INDEX => 0,
+ POINTER_TYPE => "unique" },
+ { TYPE => "POINTER",
+ POINTER_INDEX => 1,
+ POINTER_TYPE => "unique" },
+ { TYPE => "DATA" }
+ ],
+ },
+ ]
+ }
+}, { bla => "r->in.bla" }, \&print_fn);
+
+test_warnings("",
+ sub { $fn->("*r->in.bla"); });
+
+is($output, "if (*r->in.bla == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;");
+
+# Test case 5: Unknown variable
+
+$output = "";
+$fn = check_null_pointer({
+ FILE => "nofile",
+ LINE => 2,
+ PARENT => {
+ ELEMENTS => [
+ {
+ NAME => "bla",
+ LEVELS => [
+ { TYPE => "DATA" }
+ ],
+ },
+ ]
+ }
+}, { }, \&print_fn);
+
+test_warnings("nofile:2: unknown dereferenced expression `r->in.bla'\n",
+ sub { $fn->("r->in.bla"); });
+
+is($output, "if (r->in.bla == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;");