From 7727b2d5a5cbb0f6a6ec2a560e96643679744075 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Mon, 10 Feb 2020 00:15:48 +0800 Subject: [PATCH 1/5] Adding an unit test of ctypes --- Lib/ctypes/test/test_struct_fields.py | 8 ++++++++ Modules/_ctypes/cfield.c | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/ctypes/test/test_struct_fields.py b/Lib/ctypes/test/test_struct_fields.py index 8045cc82679cc1..8391b0907fd9b0 100644 --- a/Lib/ctypes/test/test_struct_fields.py +++ b/Lib/ctypes/test/test_struct_fields.py @@ -46,6 +46,14 @@ class Y(X): Y._fields_ = [] self.assertRaises(AttributeError, setattr, X, "_fields_", []) + def test_5(self): + class X(Structure): + _fields_ = (("char", c_char * 5),) + + x = X() + x.char = b'a\0b\0' + self.assertEqual(bytes(x), b'a\x00\x00\x00\x00') + # __set__ and __get__ should raise a TypeError in case their self # argument is not a ctype instance. def test___set__(self): diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index f860e6e51b2468..890554e5872702 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1294,7 +1294,8 @@ s_set(void *ptr, PyObject *value, Py_ssize_t length) } data = PyBytes_AS_STRING(value); - size = strlen(data); /* XXX Why not Py_SIZE(value)? */ + size = strlen(data); + if (size < length) { /* This will copy the terminating NUL character * if there is space for it. From f013d98ddac940341376675817273f91847ac1f4 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Mon, 10 Feb 2020 12:23:24 +0800 Subject: [PATCH 2/5] Adding bpo info in code --- Modules/_ctypes/cfield.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 890554e5872702..21da9279ec0a70 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1294,6 +1294,7 @@ s_set(void *ptr, PyObject *value, Py_ssize_t length) } data = PyBytes_AS_STRING(value); + /* bpo-39593: XXX Why not Py_SIZE(value)? */ size = strlen(data); if (size < length) { From dfa4d1b1defa4a6eb907bf0c6c3a06e557d8b3a4 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Thu, 28 May 2020 01:03:42 +0800 Subject: [PATCH 3/5] update struct_fields' case --- Lib/ctypes/test/test_struct_fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ctypes/test/test_struct_fields.py b/Lib/ctypes/test/test_struct_fields.py index 8391b0907fd9b0..ee8415f3e630c2 100644 --- a/Lib/ctypes/test/test_struct_fields.py +++ b/Lib/ctypes/test/test_struct_fields.py @@ -50,9 +50,9 @@ def test_5(self): class X(Structure): _fields_ = (("char", c_char * 5),) - x = X() + x = X(b'#' * 5) x.char = b'a\0b\0' - self.assertEqual(bytes(x), b'a\x00\x00\x00\x00') + self.assertEqual(bytes(x), b'a\x00###') # __set__ and __get__ should raise a TypeError in case their self # argument is not a ctype instance. From fd6c06c87bcf65eb02ecb28000f8a61de4be06b6 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Thu, 28 May 2020 23:38:06 +0800 Subject: [PATCH 4/5] update desc in s_set() --- Modules/_ctypes/cfield.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 8a851f3269ee7e..b51c7643733070 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1289,7 +1289,8 @@ s_set(void *ptr, PyObject *value, Py_ssize_t length) } data = PyBytes_AS_STRING(value); - /* bpo-39593: XXX Why not Py_SIZE(value)? */ + /* bpo-39593: value will be truncated after a null character on purpose. + If we use PyBytes_GET_SIZE(value) in here, it will break the feature.*/ size = strlen(data); if (size < length) { From bc3b6786f49b294fcf52f7007c51a545fb8819b2 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Fri, 29 May 2020 02:11:18 +0800 Subject: [PATCH 5/5] Update Modules/_ctypes/cfield.c Co-authored-by: Victor Stinner --- Modules/_ctypes/cfield.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index b51c7643733070..3a0d1edf11c817 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1289,8 +1289,7 @@ s_set(void *ptr, PyObject *value, Py_ssize_t length) } data = PyBytes_AS_STRING(value); - /* bpo-39593: value will be truncated after a null character on purpose. - If we use PyBytes_GET_SIZE(value) in here, it will break the feature.*/ + // bpo-39593: Use strlen() to truncate the string at the first null character. size = strlen(data); if (size < length) {