Merge ~federicoquattrin/qa-regression-testing:add_cjson_tests into qa-regression-testing:master

Proposed by Federico Quattrin
Status: Needs review
Proposed branch: ~federicoquattrin/qa-regression-testing:add_cjson_tests
Merge into: qa-regression-testing:master
Diff against target: 415 lines (+360/-0)
7 files modified
.launchpad.yaml (+15/-0)
scripts/cjson/CVE-2023-50471.c (+25/-0)
scripts/cjson/CVE-2023-50472.c (+15/-0)
scripts/cjson/README.md (+7/-0)
scripts/cjson/bug.c (+33/-0)
scripts/cjson/happy_path.c (+137/-0)
scripts/test-cjson.py (+128/-0)
Reviewer Review Type Date Requested Status
Ubuntu Security Team Pending
Review via email: mp+466195@code.launchpad.net

Commit message

add some cjson tests for CVE-2023-50471 and CVE-2023-50472

Description of the change

add some cjson tests for CVE-2023-50471 and CVE-2023-50472

To post a comment you must log in.
Revision history for this message
Allen Huang (allenpthuang) wrote :

Hi Federico, thanks for the tests! Two small things here: `vm-qrt` seems to need the executable bit set for `test-cjson.py` in order to work properly (and you can see all test-*.py have it set). The other one is you might want to add `gcc` to `QRT-Packages:` too?

Just my 2 ct, thanks again for the tests!

9ba1fd2... by Federico Quattrin

added gcc to QRT-Packages and add the execution bit to test-cjson.py

Revision history for this message
Federico Quattrin (federicoquattrin) wrote (last edit ):

> Hi Federico, thanks for the tests! Two small things here: `vm-qrt` seems to
> need the executable bit set for `test-cjson.py` in order to work properly (and
> you can see all test-*.py have it set). The other one is you might want to add
> `gcc` to `QRT-Packages:` too?
>
> Just my 2 ct, thanks again for the tests!

Hey Allen! Thanks for your feedback! I included your proposal in this commit: https://git.launchpad.net/~federicoquattrin/qa-regression-testing/commit/?id=4c10738d499e90f4e41e5ec6d7de353d78427170.

Thanks!

73e682b... by Federico Quattrin

added cjson in .launchpad.yml

02c29e5... by Federico Quattrin

skip if not 23.10 as USN-6784-1

f63feea... by Federico Quattrin

added mantic to .launchpad.yml for CJSON. Noble is still pending to be added

9868187... by Federico Quattrin

remove mantic from .launchpad.yaml

Unmerged commits

9868187... by Federico Quattrin

remove mantic from .launchpad.yaml

Succeeded
[SUCCEEDED] imagemagick:0 (build)
[SUCCEEDED] imagemagick:1 (build)
[SUCCEEDED] imagemagick:2 (build)
[SUCCEEDED] gcc-security:0 (build)
[SUCCEEDED] gcc-security:1 (build)
[SUCCEEDED] gcc-security:2 (build)
[SUCCEEDED] glibc:0 (build)
[SUCCEEDED] glibc:1 (build)
[SUCCEEDED] glibc:2 (build)
[SUCCEEDED] glibc-security:0 (build)
[SUCCEEDED] glibc-security:1 (build)
[SUCCEEDED] glibc-security:2 (build)
[SUCCEEDED] gnupg:0 (build)
[SUCCEEDED] gnupg:1 (build)
[SUCCEEDED] gnupg:2 (build)
[SUCCEEDED] sudo:0 (build)
[SUCCEEDED] sudo:1 (build)
[SUCCEEDED] sudo:2 (build)
[SUCCEEDED] git:0 (build)
[SUCCEEDED] git:1 (build)
[SUCCEEDED] git:2 (build)
[SUCCEEDED] ghostscript:0 (build)
[SUCCEEDED] ghostscript:1 (build)
[SUCCEEDED] ghostscript:2 (build)
[SUCCEEDED] busybox:0 (build)
[SUCCEEDED] busybox:1 (build)
[SUCCEEDED] busybox:2 (build)
[SUCCEEDED] coreutils:0 (build)
[SUCCEEDED] coreutils:1 (build)
[SUCCEEDED] coreutils:2 (build)
[SUCCEEDED] util-linux:0 (build)
[SUCCEEDED] util-linux:1 (build)
[SUCCEEDED] util-linux:2 (build)
[SUCCEEDED] ecdsautils:0 (build)
[SUCCEEDED] ecdsautils:1 (build)
[SUCCEEDED] ecdsautils:2 (build)
[SUCCEEDED] python-urllib3:0 (build)
[SUCCEEDED] python-urllib3:1 (build)
[SUCCEEDED] python-urllib3:2 (build)
[SUCCEEDED] amanda:0 (build)
[SUCCEEDED] amanda:1 (build)
[SUCCEEDED] cryptojs:0 (build)
[SUCCEEDED] cryptojs:1 (build)
[SUCCEEDED] cryptojs:2 (build)
[SUCCEEDED] samba:0 (build)
[SUCCEEDED] samba:1 (build)
[SUCCEEDED] samba:2 (build)
[SUCCEEDED] cjson:0 (build)
[SUCCEEDED] cjson:1 (build)
149 of 49 results
f63feea... by Federico Quattrin

added mantic to .launchpad.yml for CJSON. Noble is still pending to be added

Failed
[FAILED] imagemagick:0 (build)
[FAILED] imagemagick:1 (build)
[FAILED] imagemagick:2 (build)
[WAITING] gcc-security:0 (build)
[WAITING] gcc-security:1 (build)
[WAITING] gcc-security:2 (build)
[WAITING] glibc:0 (build)
[WAITING] glibc:1 (build)
[WAITING] glibc:2 (build)
[WAITING] glibc-security:0 (build)
[WAITING] glibc-security:1 (build)
[WAITING] glibc-security:2 (build)
[WAITING] gnupg:0 (build)
[WAITING] gnupg:1 (build)
[WAITING] gnupg:2 (build)
[WAITING] sudo:0 (build)
[WAITING] sudo:1 (build)
[WAITING] sudo:2 (build)
[WAITING] git:0 (build)
[WAITING] git:1 (build)
[WAITING] git:2 (build)
[WAITING] ghostscript:0 (build)
[WAITING] ghostscript:1 (build)
[WAITING] ghostscript:2 (build)
[WAITING] busybox:0 (build)
[WAITING] busybox:1 (build)
[WAITING] busybox:2 (build)
[WAITING] coreutils:0 (build)
[WAITING] coreutils:1 (build)
[WAITING] coreutils:2 (build)
[WAITING] util-linux:0 (build)
[WAITING] util-linux:1 (build)
[WAITING] util-linux:2 (build)
[WAITING] ecdsautils:0 (build)
[WAITING] ecdsautils:1 (build)
[WAITING] ecdsautils:2 (build)
[WAITING] python-urllib3:0 (build)
[WAITING] python-urllib3:1 (build)
[WAITING] python-urllib3:2 (build)
[WAITING] amanda:0 (build)
[WAITING] amanda:1 (build)
[WAITING] cryptojs:0 (build)
[WAITING] cryptojs:1 (build)
[WAITING] cryptojs:2 (build)
[WAITING] samba:0 (build)
[WAITING] samba:1 (build)
[WAITING] samba:2 (build)
[WAITING] cjson:0 (build)
[WAITING] cjson:1 (build)
[WAITING] cjson:2 (build)
150 of 50 results
02c29e5... by Federico Quattrin

skip if not 23.10 as USN-6784-1

73e682b... by Federico Quattrin

added cjson in .launchpad.yml

9ba1fd2... by Federico Quattrin

added gcc to QRT-Packages and add the execution bit to test-cjson.py

3eaf0d8... by Federico Quattrin

added some cjson tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.launchpad.yaml b/.launchpad.yaml
2index 0c44994..6d5519c 100644
3--- a/.launchpad.yaml
4+++ b/.launchpad.yaml
5@@ -16,6 +16,7 @@ pipeline:
6 - amanda
7 - cryptojs
8 - samba
9+ - cjson
10
11 jobs:
12 imagemagick:
13@@ -259,3 +260,17 @@ jobs:
14 DEBIAN_FRONTEND=noninteractive apt upgrade --assume-yes
15 run: |
16 ./lpcraft-runner samba
17+
18+ cjson:
19+ matrix:
20+ - series: jammy
21+ architectures: amd64
22+ - series: focal
23+ architectures: amd64
24+ packages:
25+ - sudo
26+ run-before: |
27+ DEBIAN_FRONTEND=noninteractive apt upgrade --assume-yes
28+ run: |
29+ ./lpcraft-runner cjson
30+
31diff --git a/scripts/cjson/CVE-2023-50471.c b/scripts/cjson/CVE-2023-50471.c
32new file mode 100755
33index 0000000..24ac499
34--- /dev/null
35+++ b/scripts/cjson/CVE-2023-50471.c
36@@ -0,0 +1,25 @@
37+#include <cjson/cJSON.h>
38+
39+
40+char * cve_2023_50471(){
41+ cJSON *item = cJSON_CreateString("item");
42+ cJSON *array = cJSON_CreateArray();
43+ cJSON *temp1 = cJSON_CreateString("item1");
44+ cJSON *temp2 = cJSON_CreateString("item2");
45+
46+ cJSON_AddItemToArray(array, temp1);
47+ cJSON_AddItemToArray(array, temp2);
48+
49+ // manually set the prev to be NULL to make a corrupted array
50+ temp2->prev = NULL;
51+
52+ // SEGV as after_inserted->prev is NULL, which is passed to newitem->prev, making newitem->prev->next a NULL pointer using
53+ cJSON_InsertItemInArray(array, 1, item);
54+
55+}
56+
57+int main(int argc, char **argv){
58+
59+ cve_2023_50471();
60+ return 0;
61+}
62\ No newline at end of file
63diff --git a/scripts/cjson/CVE-2023-50472.c b/scripts/cjson/CVE-2023-50472.c
64new file mode 100644
65index 0000000..be674f4
66--- /dev/null
67+++ b/scripts/cjson/CVE-2023-50472.c
68@@ -0,0 +1,15 @@
69+#include <cjson/cJSON.h>
70+
71+
72+char * cve_2023_50472(){
73+ cJSON *corruptedItem = cJSON_CreateString("corrupted");
74+ corruptedItem->valuestring = NULL;
75+ return cJSON_SetValuestring(corruptedItem, "test");
76+
77+}
78+
79+int main(int argc, char **argv){
80+
81+ cve_2023_50472();
82+ return 0;
83+}
84\ No newline at end of file
85diff --git a/scripts/cjson/README.md b/scripts/cjson/README.md
86new file mode 100644
87index 0000000..f2e8483
88--- /dev/null
89+++ b/scripts/cjson/README.md
90@@ -0,0 +1,7 @@
91+This folder contains scripts to test the cJSON library.
92+
93+Make sure you install the library by running
94+
95+sudo apt install libcjson1
96+
97+
98diff --git a/scripts/cjson/bug.c b/scripts/cjson/bug.c
99new file mode 100644
100index 0000000..2d989e9
101--- /dev/null
102+++ b/scripts/cjson/bug.c
103@@ -0,0 +1,33 @@
104+#include <cjson/cJSON.h>
105+
106+/*
107+This file tries to trigger the bug thats fixed here: https://github.com/DaveGamble/cJSON/commit/f66cbab4bfb3926ffd4c5e13f9fb6d506ee0241d
108+*/
109+
110+
111+int bug(){
112+ cJSON *item = cJSON_CreateString("item");
113+ cJSON *array = cJSON_CreateArray();
114+ cJSON *temp1 = cJSON_CreateString("item1");
115+ cJSON *temp2 = cJSON_CreateString("item2");
116+ int ret = -1;
117+
118+ cJSON_AddItemToArray(array, temp1);
119+ cJSON_AddItemToArray(array, temp2);
120+
121+ // insert item. After this operation array should have 3 items.
122+ cJSON_InsertItemInArray(array, 1, item);
123+
124+ if(cJSON_GetArraySize(array) == 3){
125+ ret = 0;
126+ }
127+
128+ // Free the memory allocated for cJSON objects
129+ cJSON_Delete(array);
130+ return ret;
131+
132+}
133+
134+int main(int argc, char **argv){
135+ return bug();
136+}
137\ No newline at end of file
138diff --git a/scripts/cjson/happy_path.c b/scripts/cjson/happy_path.c
139new file mode 100644
140index 0000000..e41e186
141--- /dev/null
142+++ b/scripts/cjson/happy_path.c
143@@ -0,0 +1,137 @@
144+#include <cjson/cJSON.h>
145+#include <stdio.h>
146+
147+char *create_monitor(void)
148+{
149+ const unsigned int resolution_numbers[3][2] = {
150+ {1280, 720},
151+ {1920, 1080},
152+ {3840, 2160}
153+ };
154+ char *string = NULL;
155+ cJSON *name = NULL;
156+ cJSON *resolutions = NULL;
157+ cJSON *resolution = NULL;
158+ cJSON *width = NULL;
159+ cJSON *height = NULL;
160+ size_t index = 0;
161+
162+ cJSON *monitor = cJSON_CreateObject();
163+ if (monitor == NULL)
164+ {
165+ goto end;
166+ }
167+
168+ name = cJSON_CreateString("Awesome 4K");
169+ if (name == NULL)
170+ {
171+ goto end;
172+ }
173+ /* after creation was successful, immediately add it to the monitor,
174+ * thereby transferring ownership of the pointer to it */
175+ cJSON_AddItemToObject(monitor, "name", name);
176+
177+ resolutions = cJSON_CreateArray();
178+ if (resolutions == NULL)
179+ {
180+ goto end;
181+ }
182+ cJSON_AddItemToObject(monitor, "resolutions", resolutions);
183+
184+ for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
185+ {
186+ resolution = cJSON_CreateObject();
187+ if (resolution == NULL)
188+ {
189+ goto end;
190+ }
191+ cJSON_AddItemToArray(resolutions, resolution);
192+
193+ width = cJSON_CreateNumber(resolution_numbers[index][0]);
194+ if (width == NULL)
195+ {
196+ goto end;
197+ }
198+ cJSON_AddItemToObject(resolution, "width", width);
199+
200+ height = cJSON_CreateNumber(resolution_numbers[index][1]);
201+ if (height == NULL)
202+ {
203+ goto end;
204+ }
205+ cJSON_AddItemToObject(resolution, "height", height);
206+ }
207+
208+ string = cJSON_Print(monitor);
209+ if (string == NULL)
210+ {
211+ fprintf(stderr, "Failed to print monitor.\n");
212+ }
213+
214+end:
215+ cJSON_Delete(monitor);
216+ return string;
217+}
218+
219+/* return 1 if the monitor supports full hd, 0 otherwise */
220+int supports_full_hd(const char * const monitor)
221+{
222+ const cJSON *resolution = NULL;
223+ const cJSON *resolutions = NULL;
224+ const cJSON *name = NULL;
225+ int status = 0;
226+ cJSON *monitor_json = cJSON_Parse(monitor);
227+ if (monitor_json == NULL)
228+ {
229+ const char *error_ptr = cJSON_GetErrorPtr();
230+ if (error_ptr != NULL)
231+ {
232+ fprintf(stderr, "Error before: %s\n", error_ptr);
233+ }
234+ status = 0;
235+ goto end;
236+ }
237+
238+ name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name");
239+
240+ resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions");
241+ cJSON_ArrayForEach(resolution, resolutions)
242+ {
243+ cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width");
244+ cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height");
245+
246+ if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height))
247+ {
248+ status = 0;
249+ goto end;
250+ }
251+
252+ if ((width->valuedouble == 1920) && (height->valuedouble == 1080))
253+ {
254+ status = 1;
255+ goto end;
256+ }
257+ }
258+
259+end:
260+ cJSON_Delete(monitor_json);
261+ return status;
262+}
263+
264+
265+int happy_path(){
266+ if(supports_full_hd(create_monitor())){
267+ fprintf(stdout, "Successfully crate and read a JSON.\n");
268+ return 0;
269+ }
270+ else{
271+ fprintf(stderr, "Failed crate and read a JSON.\n");
272+ return 1;
273+ }
274+}
275+
276+
277+int main(int argc, char **argv){
278+
279+ return happy_path();
280+}
281\ No newline at end of file
282diff --git a/scripts/test-cjson.py b/scripts/test-cjson.py
283new file mode 100755
284index 0000000..d67e822
285--- /dev/null
286+++ b/scripts/test-cjson.py
287@@ -0,0 +1,128 @@
288+#!/usr/bin/python3
289+#
290+# test-cjson.py quality assurance test script for cjson
291+# Copyright (C) 2024 Canonical Ltd.
292+# Author: Federico Quattrin <federico.quattrin@canonical.com>
293+#
294+# This program is free software: you can redistribute it and/or modify
295+# it under the terms of the GNU General Public License version 3,
296+# as published by the Free Software Foundation.
297+#
298+# This program is distributed in the hope that it will be useful,
299+# but WITHOUT ANY WARRANTY; without even the implied warranty of
300+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
301+# GNU General Public License for more details.
302+#
303+# You should have received a copy of the GNU General Public License
304+# along with this program. If not, see <http://www.gnu.org/licenses/>.
305+#
306+
307+
308+# QRT-Packages: libcjson-dev libcjson1 gcc
309+# QRT-Depends: cjson
310+
311+
312+'''
313+ In general, this test should be run in a virtual machine (VM) or possibly
314+ a chroot and not on a production machine. While efforts are made to make
315+ these tests non-destructive, there is no guarantee this script will not
316+ alter the machine. You have been warned.
317+
318+ How to run in a clean VM:
319+ $ ./make-test-tarball test-<script>.py # creates tarball in /tmp/
320+ $ scp /tmp/qrt-test-<script>.tar.gz root@vm.host:/tmp
321+ on VM:
322+ # cd /tmp ; tar zxvf ./qrt-test-<script>.tar.gz
323+ # cd /tmp/qrt-test-<script> ; ./install-packages ./test-<script>.py
324+ # ./test-<script>.py -v
325+
326+ To run in all VMs named sec*:
327+ $ vm-qrt -p sec test-<script.py>
328+
329+ ### TODO: update for ./install-packages step ###
330+ How to run in a clean schroot named 'lucid':
331+ $ schroot -c lucid -u root -- sh -c 'apt-get -y install lsb-release <QRT-Packages> && ./test-PKG.py -v'
332+'''
333+
334+from __future__ import print_function
335+
336+import os
337+import subprocess
338+import sys
339+import unittest
340+import testlib
341+from string import ascii_letters
342+import random
343+
344+
345+
346+class TestCJSON(testlib.TestlibCase):
347+ '''Test my thing.'''
348+
349+ def setUp(self):
350+ '''Set up prior to each test_* function'''
351+ self.output_name = "cjson/cjson_{}".format("".join(random.choice(ascii_letters) for i in range(5)))
352+
353+
354+ def tearDown(self):
355+ '''Clean up after each test_* function'''
356+ if os.path.isfile(self.output_name):
357+ os.remove(self.output_name)
358+
359+ @unittest.skipIf(
360+ testlib.manager.lsb_release['Release'] != 23.10,
361+ "Skipping test for Ubuntu {} since updates are in ESM".format(testlib.manager.lsb_release['Release'])
362+ )
363+ def test_cve_2023_50471(self):
364+ '''Test cve_2023_50471'''
365+ test_name = "cjson/CVE-2023-50471.c"
366+ if not self._compile_test(test_name):
367+ raise Exception(f"It was not possible to compile the source code of {test_name}")
368+ self.assertTrue(self._execute_test(self.output_name))
369+
370+ @unittest.skipIf(
371+ testlib.manager.lsb_release['Release'] != 23.10,
372+ "Skipping test for Ubuntu {} since updates are in ESM".format(testlib.manager.lsb_release['Release'])
373+ )
374+ def test_cve_2023_50472(self):
375+ '''Test cve_2023_50472'''
376+ test_name = "cjson/CVE-2023-50472.c"
377+ if not self._compile_test(test_name):
378+ raise Exception(f"It was not possible to compile the source code of {test_name}")
379+ self.assertTrue(self._execute_test(self.output_name))
380+
381+ def test_happy_path(self):
382+ '''Test if we are able to create and read JSON files using CJSON.'''
383+ test_name = "cjson/happy_path.c"
384+ if not self._compile_test(test_name):
385+ raise Exception(f"It was not possible to compile the source code of {test_name}")
386+ self.assertTrue(self._execute_test(self.output_name))
387+
388+ @unittest.skipIf(
389+ testlib.manager.lsb_release['Release'] != 23.10,
390+ "Skipping test for Ubuntu {} since updates are in ESM".format(testlib.manager.lsb_release['Release'])
391+ )
392+ def test_cve_2023_50472_bug(self):
393+ '''Test if the fix for CVE-2023-50472 has the bug fixed in this commit: https://github.com/DaveGamble/cJSON/commit/f66cbab4bfb3926ffd4c5e13f9fb6d506ee0241d.'''
394+ test_name = "cjson/bug.c"
395+ if not self._compile_test(test_name):
396+ raise Exception(f"It was not possible to compile the source code of {test_name}")
397+ self.assertTrue(self._execute_test(self.output_name))
398+
399+
400+ def _compile_test(self, test_name):
401+ subprocess.run(['gcc', '-o', self.output_name, test_name, "-lcjson"])
402+ if os.path.isfile(self.output_name):
403+ return True
404+ return False
405+
406+ @staticmethod
407+ def _execute_test(test_name):
408+ # give execution rights
409+ subprocess.run(['chmod', '+x', test_name])
410+ output = subprocess.run([test_name], capture_output=True)
411+ return output.returncode == 0
412+
413+
414+if __name__ == '__main__':
415+ unittest.main()

Subscribers

People subscribed via source and target branches