pidl: Correct string handling to use talloc and be in common
authorAndrew Bartlett <abartlet@samba.org>
Thu, 21 Apr 2016 09:44:56 +0000 (21:44 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 9 May 2016 23:43:15 +0000 (01:43 +0200)
The previous string handling assumed the python variables referenced
by PyString_AS_STRING had infinite life.  When they were re-used
the C structure started to point at unexpected things.

We now check correctly for Unicode in every case, and we always
duplicate the string with talloc_strdup()

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
pidl/lib/Parse/Pidl/Samba4/Python.pm

index 6488ac9c73b02dbcd834d3975de5bb72a3e188c9..9e39167eb7e7337cde266afdc42a76aa6e0cbf56 100644 (file)
@@ -960,6 +960,55 @@ sub assign($$$)
        }
 }
 
+sub ConvertStringFromPythonData($$$$$)
+{
+       my ($self, $mem_ctx, $py_var, $target, $fail) = @_;
+
+       $self->pidl("{");
+       $self->indent;
+       $self->pidl("const char *test_str;");
+       $self->pidl("const char *talloc_str;");
+       $self->pidl("PyObject *unicode = NULL;");
+       $self->pidl("if (PyUnicode_Check($py_var)) {");
+       $self->indent;
+       # FIXME: Use Unix charset setting rather than utf-8
+       $self->pidl("unicode = PyUnicode_AsEncodedString($py_var, \"utf-8\", \"ignore\");");
+       $self->pidl("if (unicode == NULL) {");
+       $self->indent;
+       $self->pidl("PyErr_NoMemory();");
+       $self->pidl("$fail");
+       $self->deindent;
+       $self->pidl("}");
+
+       $self->pidl("test_str = PyString_AS_STRING(unicode);");
+       $self->deindent;
+       $self->pidl("} else if (PyString_Check($py_var)) {");
+       $self->indent;
+       $self->pidl("test_str = PyString_AS_STRING($py_var);");
+       $self->deindent;
+       $self->pidl("} else {");
+       $self->indent;
+       $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected string or unicode object, got %s\", Py_TYPE($py_var)->tp_name);");
+       $self->pidl("$fail");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("talloc_str = talloc_strdup($mem_ctx, test_str);");
+       $self->pidl("if (unicode != NULL) {");
+       $self->indent;
+       $self->pidl("Py_DECREF(unicode);");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("if (talloc_str == NULL) {");
+       $self->indent;
+       $self->pidl("PyErr_NoMemory();");
+       $self->pidl("$fail");
+       $self->deindent;
+       $self->pidl("}");
+       $self->pidl("$target = talloc_str;");
+       $self->deindent;
+       $self->pidl("}");
+}
+
 sub ConvertObjectFromPythonData($$$$$$;$)
 {
        my ($self, $mem_ctx, $cvar, $ctype, $target, $fail, $location) = @_;
@@ -1114,33 +1163,17 @@ sub ConvertObjectFromPythonData($$$$$$;$)
        }
 
        if ($actual_ctype->{TYPE} eq "SCALAR" and
-               ($actual_ctype->{NAME} eq "string" or $actual_ctype->{NAME} eq "nbt_string" or $actual_ctype->{NAME} eq "nbt_name" or $actual_ctype->{NAME} eq "wrepl_nbt_name")) {
-               $self->pidl("$target = talloc_strdup($mem_ctx, PyString_AS_STRING($cvar));");
-               return;
-       }
-
-       if ($actual_ctype->{TYPE} eq "SCALAR" and ($actual_ctype->{NAME} eq "dns_string" or $actual_ctype->{NAME} eq "dns_name")) {
-               $self->pidl("$target = talloc_strdup($mem_ctx, PyString_AS_STRING($cvar));");
-               return;
-       }
-
-       if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "ipv4address") {
-               $self->pidl("$target = PyString_AS_STRING($cvar);");
-               return;
-       }
-
-       if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "ipv6address") {
-               $self->pidl("$target = PyString_AsString($cvar);");
-               return;
-       }
-
-       if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "dnsp_name") {
-               $self->pidl("$target = PyString_AS_STRING($cvar);");
-               return;
-       }
-
-       if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "dnsp_string") {
-               $self->pidl("$target = PyString_AS_STRING($cvar);");
+               ($actual_ctype->{NAME} eq "string"
+                or $actual_ctype->{NAME} eq "nbt_string"
+                or $actual_ctype->{NAME} eq "nbt_name"
+                or $actual_ctype->{NAME} eq "wrepl_nbt_name"
+                or $actual_ctype->{NAME} eq "dns_string"
+                or $actual_ctype->{NAME} eq "dnsp_string"
+                or $actual_ctype->{NAME} eq "dns_name"
+                or $actual_ctype->{NAME} eq "ipv4address"
+                or $actual_ctype->{NAME} eq "ipv6address"
+                or $actual_ctype->{NAME} eq "dnsp_name")) {
+               $self->ConvertStringFromPythonData($mem_ctx, $cvar, $target, $fail);
                return;
        }
 
@@ -1218,21 +1251,7 @@ sub ConvertObjectFromPythonLevel($$$$$$$$)
                }
 
                if (is_charset_array($e, $l)) {
-                       $self->pidl("if (PyUnicode_Check($py_var)) {");
-                       $self->indent;
-                       # FIXME: Use Unix charset setting rather than utf-8
-                       $self->pidl($var_name . " = PyString_AS_STRING(PyUnicode_AsEncodedString($py_var, \"utf-8\", \"ignore\"));");
-                       $self->deindent;
-                       $self->pidl("} else if (PyString_Check($py_var)) {");
-                       $self->indent;
-                       $self->pidl($var_name . " = PyString_AS_STRING($py_var);");
-                       $self->deindent;
-                       $self->pidl("} else {");
-                       $self->indent;
-                       $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected string or unicode object, got %s\", Py_TYPE($py_var)->tp_name);");
-                       $self->pidl("$fail");
-                       $self->deindent;
-                       $self->pidl("}");
+                       $self->ConvertStringFromPythonData($mem_ctx, $py_var, $var_name, $fail);
                } else {
                        my $counter = "$e->{NAME}_cntr_$l->{LEVEL_INDEX}";
                        $self->pidl("PY_CHECK_TYPE(&PyList_Type, $py_var, $fail);");