Re: libc: locale/rune.c input validation

2015-12-04 Thread Tobias Stoeckmann
Hi Ingo,

On Fri, Dec 04, 2015 at 12:27:39PM +0100, Ingo Schwarze wrote:
> uint32_t should be preferred because that's the POSIX type, while
> u_int32_t is not standardized.  If you are working on the file
> anyway, i'd recommend to unify all uses to uint32_t.

done.

> __LP64__ that is overly specific and not standardized.  Something
> like this maybe:
> 
>   #if SIZE_MAX <= UINT32_MAX

That sounds nice and gives a better clue why that #if is in place.
I've applied it.

> > I still cast frl_variable_len to u_int32_t for ntohl, but will cast it
> > back to int32_t for a "less than 0" check. This should properly handle
> > error cases when it's been negative to begin with.
> 
> True.  Slightly ugly, but i don't see a better way either.

Looking at UINT32_MAX usage now, I just test for var_len > INT32_MAX.


Tobias

Index: locale/rune.c
===
RCS file: /cvs/src/lib/libc/locale/rune.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 rune.c
--- locale/rune.c   25 May 2014 17:47:04 -  1.4
+++ locale/rune.c   4 Dec 2015 20:48:52 -
@@ -59,23 +59,31 @@
  * SUCH DAMAGE.
  */
 
+#include 
+#include 
 #include 
+#include 
+#include 
 #include 
-#include 
 #include 
-#include 
+#include 
 #include 
-#include 
-#include 
 #include "rune.h"
 #include "rune_local.h"
 
-static int readrange(_RuneLocale *, _RuneRange *, _FileRuneRange *, void *, 
FILE *);
+#define SAFE_ADD(x, y) \
+do {   \
+   if ((x) > SIZE_MAX - (y))   \
+   return NULL;\
+   (x) += (y); \
+} while (0);
+
+static int readrange(_RuneLocale *, _RuneRange *, uint32_t, void *, FILE *);
 static void _freeentry(_RuneRange *);
 static void _wctype_init(_RuneLocale *rl);
 
 static int
-readrange(_RuneLocale *rl, _RuneRange *rr, _FileRuneRange *frr, void *lastp,
+readrange(_RuneLocale *rl, _RuneRange *rr, uint32_t nranges, void *lastp,
FILE *fp)
 {
uint32_t i;
@@ -84,7 +92,7 @@ readrange(_RuneLocale *rl, _RuneRange *r
 
re = (_RuneEntry *)rl->rl_variable;
 
-   rr->rr_nranges = ntohl(frr->frr_nranges);
+   rr->rr_nranges = nranges;
if (rr->rr_nranges == 0) {
rr->rr_rune_ranges = NULL;
return 0;
@@ -92,16 +100,16 @@ readrange(_RuneLocale *rl, _RuneRange *r
 
rr->rr_rune_ranges = re;
for (i = 0; i < rr->rr_nranges; i++) {
+   if ((void *)re >= lastp)
+   return -1;
+
if (fread(, sizeof(fre), 1, fp) != 1)
return -1;
 
-   re->re_min = ntohl((u_int32_t)fre.fre_min);
-   re->re_max = ntohl((u_int32_t)fre.fre_max);
-   re->re_map = ntohl((u_int32_t)fre.fre_map);
+   re->re_min = ntohl((uint32_t)fre.fre_min);
+   re->re_max = ntohl((uint32_t)fre.fre_max);
+   re->re_map = ntohl((uint32_t)fre.fre_map);
re++;
-
-   if ((void *)re > lastp)
-   return -1;
}
rl->rl_variable = re;
return 0;
@@ -121,6 +129,9 @@ readentry(_RuneRange *rr, FILE *fp)
continue;
}
 
+   if (re[i].re_max < re[i].re_min)
+   goto fail;
+
l = re[i].re_max - re[i].re_min + 1;
re[i].re_rune_types = calloc(l, sizeof(_RuneType));
if (!re[i].re_rune_types) {
@@ -151,17 +162,20 @@ fail2:
 }
 
 /* XXX: temporary implementation */
-static void
+static int
 find_codeset(_RuneLocale *rl)
 {
char *top, *codeset, *tail, *ep;
 
+   if (rl->rl_variable == NULL)
+   return 0;
+
/* end of rl_variable region */
ep = (char *)rl->rl_variable;
ep += rl->rl_variable_len;
rl->rl_codeset = NULL;
if (!(top = strstr(rl->rl_variable, _RUNE_CODESET)))
-   return;
+   return 0;
tail = strpbrk(top, " \t");
codeset = top + sizeof(_RUNE_CODESET) - 1;
if (tail) {
@@ -173,6 +187,7 @@ find_codeset(_RuneLocale *rl)
*top = '\0';
rl->rl_codeset = strdup(codeset);
}
+   return (rl->rl_codeset == NULL);
 }
 
 void
@@ -183,8 +198,7 @@ _freeentry(_RuneRange *rr)
 
re = rr->rr_rune_ranges;
for (i = 0; i < rr->rr_nranges; i++) {
-   if (re[i].re_rune_types)
-   free(re[i].re_rune_types);
+   free(re[i].re_rune_types);
re[i].re_rune_types = NULL;
}
 }
@@ -209,6 +223,7 @@ _Read_RuneMagi(FILE *fp)
_RuneLocale *rl;
struct stat sb;
int x;
+   uint32_t runetype_nranges, maplower_nranges, mapupper_nranges, var_len;
 
if (fstat(fileno(fp), ) < 0)
return NULL;
@@ -225,10 +240,26 @@ _Read_RuneMagi(FILE *fp)
if (memcmp(frl.frl_magic, _RUNE_MAGIC_1, 

Re: libc: locale/rune.c input validation

2015-12-03 Thread Tobias Stoeckmann
Thanks Ingo for your extensive review! It contains lots of valuable
input for me.

I have applied all your recommendations, they make a lot of sense.

> I would suggest to use uint32_t.

Just while applying this, I noticed that the file has a mix of the
types u_int32_t and uint32_t. I took u_int32_t for now because it was
in there more often than uint32_t. What do you think?

> > +#ifndef __LP64__
> > +   if (runetype_nranges > SIZE_MAX / sizeof(_RuneEntry) ||
> > +   maplower_nranges > SIZE_MAX / sizeof(_RuneEntry) ||
> > +   mapupper_nranges > SIZE_MAX / sizeof(_RuneEntry))
> > +   return NULL;
> > +#endif
> 
> I dislike using #ifdef, it seems error-prone to me.  The checks
> are correct in any case.  Sure, if size_t is much larger than uint32_t,
> these checks can never trigger.  But why do micro-optimization here?
> Why not just delete the #ifndef/#endif?

If I skip this __LP64__ check, it triggers a gcc warning on amd64,
because the compiler tells me that the test is useless. So it's a
cosmetical thing. It's removed for now.

I still cast frl_variable_len to u_int32_t for ntohl, but will cast it
back to int32_t for a "less than 0" check. This should properly handle
error cases when it's been negative to begin with.


Tobias

Index: lib/libc/locale/rune.c
===
RCS file: /cvs/src/lib/libc/locale/rune.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 rune.c
--- locale/rune.c   25 May 2014 17:47:04 -  1.4
+++ locale/rune.c   3 Dec 2015 21:55:54 -
@@ -59,32 +59,40 @@
  * SUCH DAMAGE.
  */
 
+#include 
+#include 
 #include 
+#include 
+#include 
 #include 
-#include 
 #include 
-#include 
+#include 
 #include 
-#include 
-#include 
 #include "rune.h"
 #include "rune_local.h"
 
-static int readrange(_RuneLocale *, _RuneRange *, _FileRuneRange *, void *, 
FILE *);
+#define SAFE_ADD(x, y) \
+do {   \
+   if ((x) > SIZE_MAX - (y))   \
+   return NULL;\
+   (x) += (y); \
+} while (0);
+
+static int readrange(_RuneLocale *, _RuneRange *, u_int32_t, void *, FILE *);
 static void _freeentry(_RuneRange *);
 static void _wctype_init(_RuneLocale *rl);
 
 static int
-readrange(_RuneLocale *rl, _RuneRange *rr, _FileRuneRange *frr, void *lastp,
+readrange(_RuneLocale *rl, _RuneRange *rr, u_int32_t nranges, void *lastp,
FILE *fp)
 {
-   uint32_t i;
+   u_int32_t i;
_RuneEntry *re;
_FileRuneEntry fre;
 
re = (_RuneEntry *)rl->rl_variable;
 
-   rr->rr_nranges = ntohl(frr->frr_nranges);
+   rr->rr_nranges = nranges;
if (rr->rr_nranges == 0) {
rr->rr_rune_ranges = NULL;
return 0;
@@ -92,6 +100,9 @@ readrange(_RuneLocale *rl, _RuneRange *r
 
rr->rr_rune_ranges = re;
for (i = 0; i < rr->rr_nranges; i++) {
+   if ((void *)re >= lastp)
+   return -1;
+
if (fread(, sizeof(fre), 1, fp) != 1)
return -1;
 
@@ -99,9 +110,6 @@ readrange(_RuneLocale *rl, _RuneRange *r
re->re_max = ntohl((u_int32_t)fre.fre_max);
re->re_map = ntohl((u_int32_t)fre.fre_map);
re++;
-
-   if ((void *)re > lastp)
-   return -1;
}
rl->rl_variable = re;
return 0;
@@ -121,6 +129,9 @@ readentry(_RuneRange *rr, FILE *fp)
continue;
}
 
+   if (re[i].re_max < re[i].re_min)
+   goto fail;
+
l = re[i].re_max - re[i].re_min + 1;
re[i].re_rune_types = calloc(l, sizeof(_RuneType));
if (!re[i].re_rune_types) {
@@ -151,17 +162,20 @@ fail2:
 }
 
 /* XXX: temporary implementation */
-static void
+static int
 find_codeset(_RuneLocale *rl)
 {
char *top, *codeset, *tail, *ep;
 
+   if (rl->rl_variable == NULL)
+   return 0;
+
/* end of rl_variable region */
ep = (char *)rl->rl_variable;
ep += rl->rl_variable_len;
rl->rl_codeset = NULL;
if (!(top = strstr(rl->rl_variable, _RUNE_CODESET)))
-   return;
+   return 0;
tail = strpbrk(top, " \t");
codeset = top + sizeof(_RUNE_CODESET) - 1;
if (tail) {
@@ -173,18 +187,18 @@ find_codeset(_RuneLocale *rl)
*top = '\0';
rl->rl_codeset = strdup(codeset);
}
+   return (rl->rl_codeset == NULL);
 }
 
 void
 _freeentry(_RuneRange *rr)
 {
_RuneEntry *re;
-   uint32_t i;
+   u_int32_t i;
 
re = rr->rr_rune_ranges;
for (i = 0; i < rr->rr_nranges; i++) {
-   if (re[i].re_rune_types)
-   free(re[i].re_rune_types);
+   free(re[i].re_rune_types);
re[i].re_rune_types = NULL;
}
 }
@@ -209,6 +223,7 @@ 

libc: locale/rune.c input validation

2015-12-02 Thread Tobias Stoeckmann
Hi,

this patch adds a lot of input validation to libc/locale/rune.c.
The kind of validations are borrowed from my nls changes some
weeks ago.

I've contacted stsp@ about this. I think it's ready to get more
review from tech@. Let me know what you think!


Tobias

Index: rune.c
===
RCS file: /cvs/src/lib/libc/locale/rune.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 rune.c
--- rune.c  25 May 2014 17:47:04 -  1.4
+++ rune.c  30 Oct 2015 16:13:01 -
@@ -59,23 +59,31 @@
  * SUCH DAMAGE.
  */
 
+#include 
+#include 
 #include 
+#include 
+#include 
 #include 
-#include 
 #include 
-#include 
+#include 
 #include 
-#include 
-#include 
 #include "rune.h"
 #include "rune_local.h"
 
-static int readrange(_RuneLocale *, _RuneRange *, _FileRuneRange *, void *, 
FILE *);
+#define SAFE_ADD(x, y) \
+do {   \
+   if ((x) > SIZE_MAX - (y))   \
+   return NULL;\
+   (x) += (y); \
+} while (0);
+
+static int readrange(_RuneLocale *, _RuneRange *, rune_t, void *, FILE *);
 static void _freeentry(_RuneRange *);
 static void _wctype_init(_RuneLocale *rl);
 
 static int
-readrange(_RuneLocale *rl, _RuneRange *rr, _FileRuneRange *frr, void *lastp,
+readrange(_RuneLocale *rl, _RuneRange *rr, rune_t nranges, void *lastp,
FILE *fp)
 {
uint32_t i;
@@ -84,7 +92,7 @@ readrange(_RuneLocale *rl, _RuneRange *r
 
re = (_RuneEntry *)rl->rl_variable;
 
-   rr->rr_nranges = ntohl(frr->frr_nranges);
+   rr->rr_nranges = nranges;
if (rr->rr_nranges == 0) {
rr->rr_rune_ranges = NULL;
return 0;
@@ -92,6 +100,9 @@ readrange(_RuneLocale *rl, _RuneRange *r
 
rr->rr_rune_ranges = re;
for (i = 0; i < rr->rr_nranges; i++) {
+   if ((void *)re >= lastp)
+   return -1;
+
if (fread(, sizeof(fre), 1, fp) != 1)
return -1;
 
@@ -99,9 +110,6 @@ readrange(_RuneLocale *rl, _RuneRange *r
re->re_max = ntohl((u_int32_t)fre.fre_max);
re->re_map = ntohl((u_int32_t)fre.fre_map);
re++;
-
-   if ((void *)re > lastp)
-   return -1;
}
rl->rl_variable = re;
return 0;
@@ -121,6 +129,9 @@ readentry(_RuneRange *rr, FILE *fp)
continue;
}
 
+   if (re[i].re_max < re[i].re_min)
+   goto fail;
+
l = re[i].re_max - re[i].re_min + 1;
re[i].re_rune_types = calloc(l, sizeof(_RuneType));
if (!re[i].re_rune_types) {
@@ -151,17 +162,20 @@ fail2:
 }
 
 /* XXX: temporary implementation */
-static void
+static int
 find_codeset(_RuneLocale *rl)
 {
char *top, *codeset, *tail, *ep;
 
+   if (rl->rl_variable == NULL)
+   return 0;
+
/* end of rl_variable region */
ep = (char *)rl->rl_variable;
ep += rl->rl_variable_len;
rl->rl_codeset = NULL;
if (!(top = strstr(rl->rl_variable, _RUNE_CODESET)))
-   return;
+   return 0;
tail = strpbrk(top, " \t");
codeset = top + sizeof(_RUNE_CODESET) - 1;
if (tail) {
@@ -173,6 +187,7 @@ find_codeset(_RuneLocale *rl)
*top = '\0';
rl->rl_codeset = strdup(codeset);
}
+   return (rl->rl_codeset == NULL);
 }
 
 void
@@ -183,8 +198,7 @@ _freeentry(_RuneRange *rr)
 
re = rr->rr_rune_ranges;
for (i = 0; i < rr->rr_nranges; i++) {
-   if (re[i].re_rune_types)
-   free(re[i].re_rune_types);
+   free(re[i].re_rune_types);
re[i].re_rune_types = NULL;
}
 }
@@ -209,6 +223,7 @@ _Read_RuneMagi(FILE *fp)
_RuneLocale *rl;
struct stat sb;
int x;
+   rune_t runetype_nranges, maplower_nranges, mapupper_nranges;
 
if (fstat(fileno(fp), ) < 0)
return NULL;
@@ -225,10 +240,24 @@ _Read_RuneMagi(FILE *fp)
if (memcmp(frl.frl_magic, _RUNE_MAGIC_1, sizeof(frl.frl_magic)))
return NULL;
 
-   hostdatalen = sizeof(*rl) + ntohl((u_int32_t)frl.frl_variable_len) +
-   ntohl(frl.frl_runetype_ext.frr_nranges) * sizeof(_RuneEntry) +
-   ntohl(frl.frl_maplower_ext.frr_nranges) * sizeof(_RuneEntry) +
-   ntohl(frl.frl_mapupper_ext.frr_nranges) * sizeof(_RuneEntry);
+   /* XXX assumes rune_t = u_int32_t */
+   runetype_nranges = ntohl(frl.frl_runetype_ext.frr_nranges);
+   maplower_nranges = ntohl(frl.frl_maplower_ext.frr_nranges);
+   mapupper_nranges = ntohl(frl.frl_mapupper_ext.frr_nranges);
+
+#ifndef __LP64__
+   if (runetype_nranges > SIZE_MAX / sizeof(_RuneEntry) ||
+   maplower_nranges > SIZE_MAX / sizeof(_RuneEntry) ||
+   mapupper_nranges >