[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[MiNT] ext2 bug Was Re: another getcwd fix (includes previous patch)



On Tue, 2008-01-08 at 10:43 +0100, Vincent Rivière wrote:
> Alan Hourihane wrote :
> > I do have a testcase, but I've not delved into it to find why the
> > optimiser is causing the crash.
> > 
> > Given that the patch cures it and malloc's memory rather than from the
> > stack, I'm not inclined to go digging much further. 
> 
> I'm very frightened by that kind of bug, because the problem could be in 
> the MiNT GCC patch. If it is the case, we have to fix it. Or it may be 
> in GCC, in that case we have to make a bug report.
> 
> You have a testcase: please send it to me, I'll dig it further.

It's o.k. I had a little more time to take a closer look and it turns
out that it's a real stack smash problem caused by the ext2 xfs module.
No GCC problem for you Vincent :-)

> Anyway, your getcwd fix is really a good thing:
> - it makes the MiNTLib better, by using less stack
> - it avoids a potential GCC bug

Right. I still prefer the patch as it uses less stack.

So, I've also attached the ext2.xfs patch which is caused by the trailing
'\\' which is not calculated as part of the (len < length) test. Essentially 
we need to add 1 to len to ensure we copy the name correctly and not overwrite
memory.

I've also attached a new getcwd.c (& unx2dos.c) fix that adds __set_errno(ENOMEM)
on out of memory conditions to ensure we return errno's correctly.

Alan.
Index: mintlib/getcwd.c
===================================================================
RCS file: /mint/mintlib/mintlib/getcwd.c,v
retrieving revision 1.6
diff -u -r1.6 getcwd.c
--- mintlib/getcwd.c	8 Oct 2003 15:23:14 -0000	1.6
+++ mintlib/getcwd.c	8 Jan 2008 20:05:03 -0000
@@ -24,20 +24,29 @@
 char *
 __getcwd (char *buf, size_t size)
 {
-	const int len = (size > 0 ? size : PATH_MAX) + 16;
-	char _path[len]; /* XXX non ANSI */
-	char *path;
+	int len;
+	char *path, *_path;
 	char drv;
 	int buf_malloced = 0;
 	int r;
 
+	len = (size > 0 ? size : PATH_MAX) + 16;
+	_path = malloc(len);
+	if (!_path) {
+		__set_errno(ENOMEM);
+		return NULL;
+	}
+
 	if (!buf) {
 		if (size == 0)
 			size = PATH_MAX;
 		
 		buf = malloc (size);
-		if (!buf)
+		if (!buf) {
+			free(_path);
+			__set_errno(ENOMEM);
 			return NULL;
+		}
 		
 		buf_malloced = 1;
 	}
@@ -50,6 +59,7 @@
 
 	r = (int) Dgetcwd(path, 0, len - 2);
 	if (r != 0 && r != -ENOSYS) {
+		free(_path);
 		if (buf_malloced)
 			free(buf);
 		__set_errno (-r);
@@ -69,6 +79,14 @@
 	else
 		/* convert DOS filename to unix */
 		_dos2unx(_path, buf, size);
+
+	free(_path);
+
+	if (errno == ENAMETOOLONG) {
+		if (buf_malloced)
+			free(buf);
+		return NULL;
+	}
 	
 	if (buf_malloced) {
 		size_t len = strlen (buf) + 1;
Index: mintlib/unx2dos.c
===================================================================
RCS file: /mint/mintlib/mintlib/unx2dos.c,v
retrieving revision 1.5
diff -u -r1.5 unx2dos.c
--- mintlib/unx2dos.c	8 Oct 2003 15:23:14 -0000	1.5
+++ mintlib/unx2dos.c	8 Jan 2008 20:05:03 -0000
@@ -1,5 +1,6 @@
 
 #include <ctype.h>
+#include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
@@ -11,11 +12,15 @@
 
 /*
  * returns 0 for ordinary files, 1 for special files (like /dev/tty)
+ *
+ * or returns -1 on error, such as ENAMETOOLONG
  */
 
 int
 _unx2dos(const char *unx, char *dos, size_t len)
 {
+	register int unx_length = strlen(unx);
+	register int count = 0;
 	const char *u;
 	char *d, c;
 
@@ -76,6 +81,7 @@
 	}
 
 	while( (c = *u++) != 0 ) {
+		count++;
 		if (c == '/')
 			c = '\\';
 #if 0
@@ -91,8 +97,14 @@
 #endif
 		*d++ = c;
 		len--;
-		if (len == 0)
+		if (len == 0) {
+			if (count < unx_length) {
+				__set_errno(ENAMETOOLONG);
+				*d = 0;
+				return -1;
+			}
 			break;
+		}
 	}
 	*d = 0;
 	return 0;
@@ -101,6 +113,8 @@
 int
 _dos2unx(const char *dos, char *unx, size_t len)
 {
+	register int dos_length = strlen(dos);
+	register int count = 0;
 	register char c;
 
 	len--;			/* for terminating NUL */
@@ -133,14 +147,21 @@
 	/* convert slashes
 	 */
 	while ( (c = *dos++) != 0) {
+		count++;
 		if (c == '\\')
 			c = '/';
 		else if (__mint < 7)
 			c = tolower(c);
 		*unx++ = c;
 		len--;
-		if (len == 0)
+		if (len == 0) {
+			if (count < dos_length) {
+				__set_errno(ENAMETOOLONG);
+				*unx = 0;
+				return -1;
+			}
 			break;
+		}
 	}
 	*unx = 0;
 	return 0;
Index: ext2sys.c
===================================================================
RCS file: /mint/freemint/sys/xfs/ext2fs/ext2sys.c,v
retrieving revision 1.15
diff -u -r1.15 ext2sys.c
--- ext2sys.c	13 Jul 2007 21:32:53 -0000	1.15
+++ ext2sys.c	8 Jan 2008 19:56:35 -0000
@@ -1084,7 +1084,7 @@
 			register long i = de->name_len;
 			register char *src;
 			
-			len += i;
+			len += i + 1;
 			if (len < length)
 			{
 				src = de->name + i - 1;