Merge lp:~jamesodhunt/libnih/fix-for-bug-834813 into lp:libnih

Proposed by James Hunt
Status: Superseded
Proposed branch: lp:~jamesodhunt/libnih/fix-for-bug-834813
Merge into: lp:libnih
Diff against target: 461 lines (+407/-4) (has conflicts)
3 files modified
ChangeLog (+11/-0)
nih/string.c (+16/-4)
nih/tests/test_string.c (+380/-0)
Text conflict in ChangeLog
To merge this branch: bzr merge lp:~jamesodhunt/libnih/fix-for-bug-834813
Reviewer Review Type Date Requested Status
Scott James Remnant Needs Fixing
Review via email: mp+73070@code.launchpad.net

This proposal has been superseded by a proposal from 2011-08-30.

Description of the change

  * ChangeLog: updated.
  * nih/string.c (nih_str_split): Fixes to avoid over-running
    input string and also returning an empty string array entry
    when repeat is true (LP: #834813).
  * nih/tests/test_string.c (test_str_split): Added a lot of new
    tests for nih_str_split().

To post a comment you must log in.
Revision history for this message
Scott James Remnant (scott) wrote :

"str && *str" shouldn't be necessary, this function already asserts "str != NULL"

Have you checked other uses of strchr() in the code, I probably make the same mistake all over the place - believing it would return NULL if given '\0'

I'd like that final if() cleaned up, it doesn't make sense what you're doing there - perhaps splitting out some informationally named temporary variables and checking those would make more sense, or using some other method

review: Needs Fixing
1050. By Scott James Remnant (Canonical)

* nih/config.c, nih/error.h, nih/io.c, nih/test_files.h: Correct
typos in comments.

1051. By Scott James Remnant (Canonical)

Bump copyright dates to 2011

1052. By Scott James Remnant (Canonical)

* nih/io.c (nih_io_select_fds): Ensure number of fds being managed
is within limits.

Revision history for this message
James Hunt (jamesodhunt) wrote :

> "str && *str" shouldn't be necessary, this function already asserts "str !=
> NULL"
>
> Have you checked other uses of strchr() in the code, I probably make the same
> mistake all over the place - believing it would return NULL if given '\0'
>
> I'd like that final if() cleaned up, it doesn't make sense what you're doing
> there - perhaps splitting out some informationally named temporary variables
> and checking those would make more sense, or using some other method
Hi Scott,

Firstly, I've raised a bug on strchr(3) and memchr(3) since they do not currently specify the behaviour should the char to search for be '\0':

https://bugzilla.kernel.org/show_bug.cgi?id=42042

I've reworked (simplified) the change to nih_str_split(). Note that the "continue" is safe since we consume any and all multiple delimiter characters on the next loop iteration.

The assert protects the function initially, but since we're incrementing 'str' we do need atleast the first "str && *str" check...

   while (repeat && str && *str && strchr (delim, *str))

...since without it, we overrun the string, and the new tests will fail. The subsequent "str && *str"'s may not be strictly required, but as this bug was rather subtle anyway, I'd be tempted to err on the side of caution.

I'll resubmit the MP...

Cheers,

James.

Revision history for this message
Scott James Remnant (scott) wrote :

On Tue, Aug 30, 2011 at 7:33 AM, James Hunt <email address hidden>wrote:

> Firstly, I've raised a bug on strchr(3) and memchr(3) since they do not
> currently specify the behaviour should the char to search for be '\0':
>
> https://bugzilla.kernel.org/show_bug.cgi?id=42042
>
>
Interestingly the OS X man pages do specify:

     The strchr() function locates the first occurrence of c (converted to a
     char) in the string pointed to by s. The terminating null character is
     considered to be part of the string; therefore if c is `\0', the func-
     tions locate the terminating `\0'.

Scott

1053. By James Hunt

* ChangeLog: Updated.
* nih/string.c (nih_str_split): Fixes to avoid over-running
  input string and also returning an empty string array entry
  when repeat is true (LP: #834813).
* nih/tests/test_string.c (test_str_split): Added a lot of new
  tests for nih_str_split().

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2011-08-26 18:31:43 +0000
3+++ ChangeLog 2011-08-30 14:13:47 +0000
4@@ -1,3 +1,4 @@
5+<<<<<<< TREE
6 2011-08-26 James Hunt <james.hunt@ubuntu.com>
7
8 * nih/io.c (nih_io_select_fds): Ensure number of fds being managed
9@@ -6,6 +7,16 @@
10 * nih/config.c, nih/error.h, nih/io.c, nih/test_files.h: Correct
11 typos in comments.
12
13+=======
14+2011-08-30 James Hunt <james.hunt@ubuntu.com>
15+
16+ * nih/string.c (nih_str_split): Fixes to avoid over-running
17+ input string and also returning an empty string array entry
18+ when repeat is true (LP: #834813).
19+ * nih/tests/test_string.c (test_str_split): Added a lot of new
20+ tests for nih_str_split().
21+
22+>>>>>>> MERGE-SOURCE
23 2011-06-20 James Hunt <james.hunt@ubuntu.com>
24
25 * nih/watch.c (nih_watch_handle): Handle non-directory watches;
26
27=== modified file 'nih/string.c'
28--- nih/string.c 2009-06-23 09:29:37 +0000
29+++ nih/string.c 2011-08-30 14:13:47 +0000
30@@ -403,17 +403,29 @@
31
32 while (*str) {
33 const char *ptr;
34+ size_t toklen;
35
36- /* Skip initial delimiters */
37- while (repeat && strchr (delim, *str))
38+ /* Skip initial delimiters taking care not to walk
39+ * beyond the end of the string
40+ */
41+ while (repeat && str && *str && strchr (delim, *str))
42 str++;
43
44 /* Find the end of the token */
45 ptr = str;
46- while (*str && (! strchr (delim, *str)))
47+ while (str && *str && (! strchr (delim, *str)))
48 str++;
49
50- if (! nih_str_array_addn (&array, parent, &len,
51+ toklen = str - ptr;
52+
53+ /* Don't create an empty string array element in repeat
54+ * mode if there is no token (as a result of a
55+ * duplicated delimiter character).
56+ */
57+ if (repeat && !toklen)
58+ continue;
59+
60+ if (str && ! nih_str_array_addn (&array, parent, &len,
61 ptr, str - ptr)) {
62 nih_free (array);
63 return NULL;
64
65=== modified file 'nih/tests/test_string.c'
66--- nih/tests/test_string.c 2009-11-28 20:25:03 +0000
67+++ nih/tests/test_string.c 2011-08-30 14:13:47 +0000
68@@ -619,6 +619,215 @@
69 nih_free (array);
70 }
71
72+ TEST_FEATURE ("with no repeat and multiple identical delimiter "
73+ "characters at string start");
74+ TEST_ALLOC_FAIL {
75+ array = nih_str_split (NULL, "\t\tthis is a test", " \t", FALSE);
76+
77+ if (test_alloc_failed) {
78+ TEST_EQ_P (array, NULL);
79+ continue;
80+ }
81+
82+ TEST_ALLOC_SIZE (array, sizeof (char *) * 7);
83+ for (i = 0; i < 6; i++)
84+ TEST_ALLOC_PARENT (array[i], array);
85+
86+ TEST_EQ_STR (array[0], "");
87+ TEST_EQ_STR (array[1], "");
88+ TEST_EQ_STR (array[2], "this");
89+ TEST_EQ_STR (array[3], "is");
90+ TEST_EQ_STR (array[4], "a");
91+ TEST_EQ_STR (array[5], "test");
92+ TEST_EQ_P (array[6], NULL);
93+
94+ nih_free (array);
95+ }
96+
97+ TEST_FEATURE ("with no repeat and multiple different delimiter "
98+ "characters at string start");
99+ TEST_ALLOC_FAIL {
100+ array = nih_str_split (NULL, " \tthis is a test", " \t", FALSE);
101+
102+ if (test_alloc_failed) {
103+ TEST_EQ_P (array, NULL);
104+ continue;
105+ }
106+
107+ TEST_ALLOC_SIZE (array, sizeof (char *) * 7);
108+ for (i = 0; i < 6; i++)
109+ TEST_ALLOC_PARENT (array[i], array);
110+
111+ TEST_EQ_STR (array[0], "");
112+ TEST_EQ_STR (array[1], "");
113+ TEST_EQ_STR (array[2], "this");
114+ TEST_EQ_STR (array[3], "is");
115+ TEST_EQ_STR (array[4], "a");
116+ TEST_EQ_STR (array[5], "test");
117+ TEST_EQ_P (array[6], NULL);
118+
119+ nih_free (array);
120+ }
121+
122+ TEST_FEATURE ("with no repeat and multiple identical delimiter "
123+ "characters within string");
124+ TEST_ALLOC_FAIL {
125+ array = nih_str_split (NULL, "this is a\t\ttest", " \t", FALSE);
126+
127+ if (test_alloc_failed) {
128+ TEST_EQ_P (array, NULL);
129+ continue;
130+ }
131+
132+ TEST_ALLOC_SIZE (array, sizeof (char *) * 8);
133+ for (i = 0; i < 7; i++)
134+ TEST_ALLOC_PARENT (array[i], array);
135+
136+ TEST_EQ_STR (array[0], "this");
137+ TEST_EQ_STR (array[1], "is");
138+ TEST_EQ_STR (array[2], "");
139+ TEST_EQ_STR (array[3], "");
140+ TEST_EQ_STR (array[4], "a");
141+ TEST_EQ_STR (array[5], "");
142+ TEST_EQ_STR (array[6], "test");
143+ TEST_EQ_P (array[7], NULL);
144+
145+ nih_free (array);
146+ }
147+
148+ TEST_FEATURE ("with no repeat and multiple different delimiter "
149+ "characters within string");
150+ TEST_ALLOC_FAIL {
151+ array = nih_str_split (NULL, "this is \n\ta\ttest", " \t\n", FALSE);
152+
153+ if (test_alloc_failed) {
154+ TEST_EQ_P (array, NULL);
155+ continue;
156+ }
157+
158+ TEST_ALLOC_SIZE (array, sizeof (char *) * 7);
159+ for (i = 0; i < 6; i++)
160+ TEST_ALLOC_PARENT (array[i], array);
161+
162+ TEST_EQ_STR (array[0], "this");
163+ TEST_EQ_STR (array[1], "is");
164+ TEST_EQ_STR (array[2], "");
165+ TEST_EQ_STR (array[3], "");
166+ TEST_EQ_STR (array[4], "a");
167+ TEST_EQ_STR (array[5], "test");
168+ TEST_EQ_P (array[6], NULL);
169+
170+ nih_free (array);
171+ }
172+
173+ TEST_FEATURE ("with no repeat and multiple identical delimiter "
174+ "characters at string end");
175+ TEST_ALLOC_FAIL {
176+ array = nih_str_split (NULL, "this is a test ", " \t", FALSE);
177+
178+ if (test_alloc_failed) {
179+ TEST_EQ_P (array, NULL);
180+ continue;
181+ }
182+
183+ TEST_ALLOC_SIZE (array, sizeof (char *) * 6);
184+ for (i = 0; i < 5; i++)
185+ TEST_ALLOC_PARENT (array[i], array);
186+
187+ TEST_EQ_STR (array[0], "this");
188+ TEST_EQ_STR (array[1], "is");
189+ TEST_EQ_STR (array[2], "a");
190+ TEST_EQ_STR (array[3], "test");
191+ TEST_EQ_STR (array[4], "");
192+ TEST_EQ_P (array[5], NULL);
193+
194+ nih_free (array);
195+ }
196+
197+ TEST_FEATURE ("with no repeat and multiple different delimiter "
198+ "characters at string end");
199+ TEST_ALLOC_FAIL {
200+ array = nih_str_split (NULL, "this is a test \t", " \t", FALSE);
201+
202+ if (test_alloc_failed) {
203+ TEST_EQ_P (array, NULL);
204+ continue;
205+ }
206+
207+ TEST_ALLOC_SIZE (array, sizeof (char *) * 6);
208+ for (i = 0; i < 5; i++)
209+ TEST_ALLOC_PARENT (array[i], array);
210+
211+ TEST_EQ_STR (array[0], "this");
212+ TEST_EQ_STR (array[1], "is");
213+ TEST_EQ_STR (array[2], "a");
214+ TEST_EQ_STR (array[3], "test");
215+ TEST_EQ_STR (array[4], "");
216+ TEST_EQ_P (array[5], NULL);
217+
218+ nih_free (array);
219+ }
220+
221+ TEST_FEATURE ("with no repeat and multiple identical delimiter "
222+ "characters at beginning, middle and end of string");
223+ TEST_ALLOC_FAIL {
224+ array = nih_str_split (NULL, " this is\n\n\na test\t\t\t", " \t\n", FALSE);
225+
226+ if (test_alloc_failed) {
227+ TEST_EQ_P (array, NULL);
228+ continue;
229+ }
230+
231+ TEST_ALLOC_SIZE (array, sizeof (char *) * 12);
232+ for (i = 0; i < 11; i++)
233+ TEST_ALLOC_PARENT (array[i], array);
234+
235+ TEST_EQ_STR (array[0], "");
236+ TEST_EQ_STR (array[1], "");
237+ TEST_EQ_STR (array[2], "");
238+ TEST_EQ_STR (array[3], "this");
239+ TEST_EQ_STR (array[4], "is");
240+ TEST_EQ_STR (array[5], "");
241+ TEST_EQ_STR (array[6], "");
242+ TEST_EQ_STR (array[7], "a");
243+ TEST_EQ_STR (array[8], "test");
244+ TEST_EQ_STR (array[9], "");
245+ TEST_EQ_STR (array[10], "");
246+ TEST_EQ_P (array[11], NULL);
247+
248+ nih_free (array);
249+ }
250+
251+ TEST_FEATURE ("with no repeat and multiple different delimiter "
252+ "characters at beginning, middle and end of string");
253+ TEST_ALLOC_FAIL {
254+ array = nih_str_split (NULL, ": \nthis is\t \n:a test:\n ", "\n :\t", FALSE);
255+
256+ if (test_alloc_failed) {
257+ TEST_EQ_P (array, NULL);
258+ continue;
259+ }
260+
261+ TEST_ALLOC_SIZE (array, sizeof (char *) * 13);
262+ for (i = 0; i < 12; i++)
263+ TEST_ALLOC_PARENT (array[i], array);
264+
265+ TEST_EQ_STR (array[0], "");
266+ TEST_EQ_STR (array[1], "");
267+ TEST_EQ_STR (array[2], "");
268+ TEST_EQ_STR (array[3], "this");
269+ TEST_EQ_STR (array[4], "is");
270+ TEST_EQ_STR (array[5], "");
271+ TEST_EQ_STR (array[6], "");
272+ TEST_EQ_STR (array[7], "");
273+ TEST_EQ_STR (array[8], "a");
274+ TEST_EQ_STR (array[9], "test");
275+ TEST_EQ_STR (array[10], "");
276+ TEST_EQ_STR (array[11], "");
277+ TEST_EQ_P (array[12], NULL);
278+
279+ nih_free (array);
280+ }
281
282 /* Check that we can split a string treating multiple consecutive
283 * matching characters as a single separator to be skipped.
284@@ -645,6 +854,177 @@
285 nih_free (array);
286 }
287
288+ /* Check that we can split a string containing multiple
289+ * occurences of one of the delimiter characters at the
290+ * beginning of the string.
291+ */
292+ TEST_FEATURE ("with repeat and multiple identical adjacent delimiter characters at string start");
293+ TEST_ALLOC_FAIL {
294+ array = nih_str_split (NULL, "\n\nhello", " \t\r\n", TRUE);
295+
296+ if (test_alloc_failed) {
297+ TEST_EQ_P (array, NULL);
298+ continue;
299+ }
300+
301+ TEST_ALLOC_SIZE (array, sizeof (char *) * 2);
302+ for (i = 0; i < 1; i++)
303+ TEST_ALLOC_PARENT (array[i], array);
304+
305+ TEST_EQ_STR (array[0], "hello");
306+ TEST_EQ_P (array[1], NULL);
307+
308+ nih_free (array);
309+ }
310+
311+ TEST_FEATURE ("with repeat and multiple different adjacent delimiter characters at string start");
312+ TEST_ALLOC_FAIL {
313+ array = nih_str_split (NULL, "\n\r hello", " \t\r\n", TRUE);
314+
315+ if (test_alloc_failed) {
316+ TEST_EQ_P (array, NULL);
317+ continue;
318+ }
319+
320+ TEST_ALLOC_SIZE (array, sizeof (char *) * 2);
321+ for (i = 0; i < 1; i++)
322+ TEST_ALLOC_PARENT (array[i], array);
323+
324+ TEST_EQ_STR (array[0], "hello");
325+ TEST_EQ_P (array[1], NULL);
326+
327+ nih_free (array);
328+ }
329+
330+ TEST_FEATURE ("with repeat and multiple identical adjacent delimiter "
331+ "characters within string");
332+ TEST_ALLOC_FAIL {
333+ array = nih_str_split (NULL, "hello\n\rworld", " \t\n\r", TRUE);
334+
335+ if (test_alloc_failed) {
336+ TEST_EQ_P (array, NULL);
337+ continue;
338+ }
339+
340+ TEST_ALLOC_SIZE (array, sizeof (char *) * 3);
341+ for (i = 0; i < 2; i++)
342+ TEST_ALLOC_PARENT (array[i], array);
343+
344+ TEST_EQ_STR (array[0], "hello");
345+ TEST_EQ_STR (array[1], "world");
346+ TEST_EQ_P (array[2], NULL);
347+
348+ nih_free (array);
349+ }
350+
351+ TEST_FEATURE ("with repeat and multiple different adjacent delimiter "
352+ "characters within string");
353+ TEST_ALLOC_FAIL {
354+ array = nih_str_split (NULL, "hello\n\r\tworld", " \t\n\r", TRUE);
355+
356+ if (test_alloc_failed) {
357+ TEST_EQ_P (array, NULL);
358+ continue;
359+ }
360+
361+ TEST_ALLOC_SIZE (array, sizeof (char *) * 3);
362+ for (i = 0; i < 2; i++)
363+ TEST_ALLOC_PARENT (array[i], array);
364+
365+ TEST_EQ_STR (array[0], "hello");
366+ TEST_EQ_STR (array[1], "world");
367+ TEST_EQ_P (array[2], NULL);
368+
369+ nih_free (array);
370+ }
371+
372+ TEST_FEATURE ("with repeat and multiple identical adjacent delimiter "
373+ "characters at string end");
374+ TEST_ALLOC_FAIL {
375+ array = nih_str_split (NULL, "hello\n\n\n\n\n\n\n", " \t\r\n", TRUE);
376+
377+ if (test_alloc_failed) {
378+ TEST_EQ_P (array, NULL);
379+ continue;
380+ }
381+
382+ TEST_ALLOC_SIZE (array, sizeof (char *) * 2);
383+ for (i = 0; i < 1; i++)
384+ TEST_ALLOC_PARENT (array[i], array);
385+
386+ TEST_EQ_STR (array[0], "hello");
387+ TEST_EQ_P (array[1], NULL);
388+
389+ nih_free (array);
390+ }
391+
392+ TEST_FEATURE ("with repeat and multiple different adjacent delimiter "
393+ "characters at string end");
394+ TEST_ALLOC_FAIL {
395+ array = nih_str_split (NULL, "hello \r\t\r\t\n ", " \t\r\n", TRUE);
396+
397+ if (test_alloc_failed) {
398+ TEST_EQ_P (array, NULL);
399+ continue;
400+ }
401+
402+ TEST_ALLOC_SIZE (array, sizeof (char *) * 2);
403+ for (i = 0; i < 1; i++)
404+ TEST_ALLOC_PARENT (array[i], array);
405+
406+ TEST_EQ_STR (array[0], "hello");
407+ TEST_EQ_P (array[1], NULL);
408+
409+ nih_free (array);
410+ }
411+
412+ TEST_FEATURE ("with repeat and multiple identical adjacent delimiter "
413+ "characters at beginning, middle and end of string");
414+ TEST_ALLOC_FAIL {
415+ array = nih_str_split (NULL,
416+ " hello\n\n\n, world\n\n\n",
417+ "\r\t\n ", TRUE);
418+
419+ if (test_alloc_failed) {
420+ TEST_EQ_P (array, NULL);
421+ continue;
422+ }
423+
424+ TEST_ALLOC_SIZE (array, sizeof (char *) * 4);
425+ for (i = 0; i < 3; i++)
426+ TEST_ALLOC_PARENT (array[i], array);
427+
428+ TEST_EQ_STR (array[0], "hello");
429+ TEST_EQ_STR (array[1], ",");
430+ TEST_EQ_STR (array[2], "world");
431+ TEST_EQ_P (array[3], NULL);
432+
433+ nih_free (array);
434+ }
435+
436+ TEST_FEATURE ("with repeat and multiple different adjacent delimiter "
437+ "characters at beginning, middle and end of string");
438+ TEST_ALLOC_FAIL {
439+ array = nih_str_split (NULL,
440+ "\n \r\thello\n\n\r , \n\t\rworld\t \r\n \n",
441+ " \t\n\r", TRUE);
442+
443+ if (test_alloc_failed) {
444+ TEST_EQ_P (array, NULL);
445+ continue;
446+ }
447+
448+ TEST_ALLOC_SIZE (array, sizeof (char *) * 4);
449+ for (i = 0; i < 3; i++)
450+ TEST_ALLOC_PARENT (array[i], array);
451+
452+ TEST_EQ_STR (array[0], "hello");
453+ TEST_EQ_STR (array[1], ",");
454+ TEST_EQ_STR (array[2], "world");
455+ TEST_EQ_P (array[3], NULL);
456+
457+ nih_free (array);
458+ }
459
460 /* Check that we can give an empty string, and end up with a
461 * one-element array that only contains a NULL pointer.

Subscribers

People subscribed via source and target branches