Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable strict parsing #105

Open
MarioMalenica opened this issue Apr 15, 2019 · 1 comment
Open

Enable strict parsing #105

MarioMalenica opened this issue Apr 15, 2019 · 1 comment

Comments

@MarioMalenica
Copy link

MarioMalenica commented Apr 15, 2019

Enable more thorough check of parsing overflows and errors.
It will check for trailing junk characters in number strings.
Whitespace characters are allowed (isspace()), anything else will result in a parsing error.

Pull request across different sites doesn't work with GitHub.
My cloned repo is on BitBucket.
Here are some changes in patch file format.

0001-Add-more-thorough-number-parsing-checks.-Fix-a-coupl.txt

@lmoellendorf
Copy link
Collaborator

Said patch for easier review:

From 3b29914200c76d8b867fbc31a499e6104119d90e Mon Sep 17 00:00:00 2001
From: Mario Malenica <[email protected]>
Date: Sun, 10 Feb 2019 21:32:13 +0100
Subject: [PATCH] Add more thorough number parsing checks. Fix a couple of
 potential bugs.

---
 Makefile               |  6 ++++
 src/dictionary.c       | 15 ++++++++
 src/iniparser.c        | 98 ++++++++++++++++++++++++++++++++++++++++++++++----
 test/test_dictionary.c |  1 +
 4 files changed, 113 insertions(+), 7 deletions(-)

diff --git a/Makefile b/Makefile
index a5a4ab2..db0a8b0 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,12 @@
 CC      ?= gcc
 
 CFLAGS  += -fPIC -Wall -Wextra -ansi -pedantic
+#Enable more thorough check of parsing overflows and errors.
+#It will check for trailing junk characters in number strings.
+#Whitespace characters are allowed (isspace()), anything else will result
+#in a parsing error.
+CFLAGS  += -DINIPARSER_STRICT_PARSER
+
 ifndef DEBUG
 ADDITIONAL_CFLAGS  ?= -O2
 else
diff --git a/src/dictionary.c b/src/dictionary.c
index cb7ccd4..03b334b 100644
--- a/src/dictionary.c
+++ b/src/dictionary.c
@@ -159,8 +159,23 @@ dictionary * dictionary_new(size_t size)
     if (d) {
         d->size = size ;
         d->val  = (char**) calloc(size, sizeof *d->val);
+        if(d->val == NULL) {
+        	free(d);
+        	return NULL;
+        }
         d->key  = (char**) calloc(size, sizeof *d->key);
+        if(d->key == NULL) {
+        	free(d->val);
+        	free(d);
+        	return NULL;
+        }
         d->hash = (unsigned*) calloc(size, sizeof *d->hash);
+        if(d->hash == NULL) {
+        	free(d->key);
+        	free(d->val);
+        	free(d);
+        	return NULL;
+        }
     }
     return d ;
 }
diff --git a/src/iniparser.c b/src/iniparser.c
index fffdf9f..8264117 100644
--- a/src/iniparser.c
+++ b/src/iniparser.c
@@ -7,8 +7,14 @@
 */
 /*--------------------------------------------------------------------------*/
 /*---------------------------- Includes ------------------------------------*/
+#include <stdio.h>
 #include <ctype.h>
 #include <stdarg.h>
+#if defined(INIPARSER_STRICT_PARSER)
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#endif /* INIPARSER_STRICT_PARSER */
 #include "iniparser.h"
 
 /*---------------------------- Defines -------------------------------------*/
@@ -48,7 +54,7 @@ static const char * strlwc(const char * in, char *out, unsigned len)
 
     if (in==NULL || out == NULL || len==0) return NULL ;
     i=0 ;
-    while (in[i] != '\0' && i < len-1) {
+    while ((i < len-1) && (in[i] != '\0')) {
         out[i] = (char)tolower((int)in[i]);
         i++ ;
     }
@@ -444,20 +450,54 @@ const char * iniparser_getstring(const dictionary * d, const char * key, const c
   "042"     ->  34 (octal -> decimal)
   "0x42"    ->  66 (hexa  -> decimal)
 
-  Warning: the conversion may overflow in various ways. Conversion is
-  totally outsourced to strtol(), see the associated man page for overflow
-  handling.
-
   Credits: Thanks to A. Becker for suggesting strtol()
  */
 /*--------------------------------------------------------------------------*/
 long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
 {
     const char * str ;
+#if defined(INIPARSER_STRICT_PARSER)
+    long int tmp_num;
+    int tmp_errno;
+    char *endptr = NULL;
+#endif /* INIPARSER_STRICT_PARSER */
 
     str = iniparser_getstring(d, key, INI_INVALID_KEY);
     if (str==INI_INVALID_KEY) return notfound ;
+
+#if defined(INIPARSER_STRICT_PARSER)
+    /* In case of an error strtol() can return 0,
+     * LONG_MIN or LONG_MAX and errno will be set:
+     * http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html
+     */
+    tmp_errno = errno;
+    tmp_num = strtol(str, &endptr, 0);
+    /* No valid string supplied */
+    if(endptr == NULL || endptr == str)
+    	return notfound;
+
+    /* In case if there are any whitespace character after the
+     * number, endptr will point to the first one.
+     * Ignore whitespace at the end of the string,
+     * but check if there are any junk characters,
+     * and return notfound in case if there are any.*/
+    while(*endptr) {
+    	if(isspace(*endptr)) {
+    		endptr++;
+    	} else {
+    		errno = EINVAL;
+    		return notfound;
+    	}
+    }
+
+    if((tmp_num == 0 || tmp_num == LONG_MIN ||
+    		tmp_num == LONG_MAX) && tmp_errno != errno) {
+    	tmp_num = notfound;
+    }
+    return tmp_num;
+#else
     return strtol(str, NULL, 0);
+#endif /* INIPARSER_STRICT_PARSER */
 }
 
 
@@ -490,7 +530,17 @@ long int iniparser_getlongint(const dictionary * d, const char * key, long int n
 /*--------------------------------------------------------------------------*/
 int iniparser_getint(const dictionary * d, const char * key, int notfound)
 {
+#if defined(INIPARSER_STRICT_PARSER)
+	long int res = iniparser_getlongint(d, key, notfound);
+	/* Check if the result is within valid range for an integer */
+	if(res != notfound && (res < INT_MIN || res > INT_MAX)) {
+		res = notfound;
+		errno = ERANGE;
+	}
+	return (int)res;
+#else
     return (int)iniparser_getlongint(d, key, notfound);
+#endif
 }
 
 /*-------------------------------------------------------------------------*/
@@ -503,16 +553,50 @@ int iniparser_getint(const dictionary * d, const char * key, int notfound)
 
   This function queries a dictionary for a key. A key as read from an
   ini file is given as "section:key". If the key cannot be found,
-  the notfound value is returned.
+  or if there is an error while parsing then notfound value will be returned.
  */
 /*--------------------------------------------------------------------------*/
 double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
 {
     const char * str ;
-
+#if defined(INIPARSER_STRICT_PARSER)
+    char *endptr = NULL;
+    double tmp_num;
+    int tmp_errno;
+#endif /* INIPARSER_STRICT_PARSER */
     str = iniparser_getstring(d, key, INI_INVALID_KEY);
     if (str==INI_INVALID_KEY) return notfound ;
+
+#if defined(INIPARSER_STRICT_PARSER)
+    tmp_errno = errno;
+    tmp_num = strtod(str, &endptr);
+    /* No valid string supplied */
+    if(endptr == NULL || endptr == str)
+    	return notfound;
+
+    /* In case if there are any whitespace character after the
+     * number, endptr will point to the first one.
+     * Ignore whitespace at the end of the string,
+     * but check if there are any junk characters,
+     * and return notfound in case if there are any.*/
+    while(*endptr) {
+    	if(isspace(*endptr)) {
+    		endptr++;
+    	} else {
+    		errno = EINVAL;
+    		return notfound;
+    	}
+    }
+
+    if((tmp_num == 0 || tmp_num == HUGE_VAL ||
+    		tmp_num == -(HUGE_VAL)) && tmp_errno != errno) {
+    	tmp_num = notfound;
+    }
+    return tmp_num;
+#else
     return atof(str);
+#endif /* INIPARSER_STRICT_PARSER */
+
 }
 
 /*-------------------------------------------------------------------------*/
diff --git a/test/test_dictionary.c b/test/test_dictionary.c
index f89f1c1..bbe045d 100644
--- a/test/test_dictionary.c
+++ b/test/test_dictionary.c
@@ -124,6 +124,7 @@ static char *get_dump(dictionary *d)
     }
     if (fread(dump_buff, 1, dump_size, fd) != (size_t)dump_size) {
         fclose(fd);
+        free(dump_buff);
         return NULL;
     }
 
-- 
2.7.4

@lmoellendorf lmoellendorf changed the title Patch file with a couple of fixes Enable strict parsing Mar 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants