diff mbox series

[1/2] discover: Allow for empty paths

Message ID 20210115010849.4102-1-klaus@linux.vnet.ibm.com
State New
Headers show
Series [1/2] discover: Allow for empty paths | expand

Commit Message

Klaus Heinrich Kiwi Jan. 15, 2021, 1:08 a.m. UTC
Some grub tests involve a (device)/path structure, where it is
actually legal to have an empty path. Adjust join_path() and
dependant functions to allow for empty pathnames.

Signed-off-by: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com>
---
 discover/grub2/builtins.c |  8 ++++----
 discover/grub2/grub2.c    | 19 ++++++++++---------
 discover/paths.c          |  8 +++++---
 test/parser/utils.c       |  2 +-
 4 files changed, 20 insertions(+), 17 deletions(-)

Comments

Jeremy Kerr Jan. 20, 2021, 5:03 a.m. UTC | #1
Hi Klaus,

Some grub tests involve a (device)/path structure, where it is
actually legal to have an empty path. Adjust join_path() and
dependant functions to allow for empty pathnames.

Makes sense! Some comments on the implementation though.

In general, we do need to decide whether this situation gives a NULL
value for ->path, or an empty string for ->path. You've gone for the
former here (which I think is the correct choice), but there are are a
lot of assumptions that ->path will be non-null.

We will need to change the cases that assume ->path is non-null -
currently, there are cases where this will result in a NULL 'b'
argument to join_paths, which tries to strlen(b), and crashes.

--- a/discover/grub2/builtins.c
+++ b/discover/grub2/builtins.c
@@ -216,7 +216,7 @@ static int parse_to_device_path(struct grub2_script
*script,
                return -1;
 
        *devp = dev;
-       *pathp = talloc_strdup(script, file->path);
+       *pathp = !file->path ? NULL : talloc_strdup(script, file-
>path);

talloc_strdup() does the exactly this check, you don't need this
change.

diff --git a/discover/grub2/grub2.c b/discover/grub2/grub2.c
index b176ce2..52c75e9 100644
--- a/discover/grub2/grub2.c
+++ b/discover/grub2/grub2.c
@@ -118,7 +118,6 @@ struct grub2_file *grub2_parse_file(struct
grub2_script *script,
                const char *str)
 {
        struct grub2_file *file;
-       size_t dev_len;
        char *pos;
 
        if (!str)
@@ -129,6 +128,7 @@ struct grub2_file *grub2_parse_file(struct
grub2_script *script,
        if (*str != '(') {
                /* just a path - no device, return path as-is */
                file->path = talloc_strdup(file, str);
+               file->dev = NULL;

This is already covered by the talloc_zero.

 
        } else {
                /* device plus path - split into components */
@@ -137,17 +137,18 @@ struct grub2_file *grub2_parse_file(struct
grub2_script *script,
 
                /* no closing bracket, or zero-length path? */
                if (!pos || *(pos+1) == '\0') {
-                       talloc_free(file);
-                       return NULL;
+                       file->path = NULL;
+               }
+               else {
+                       file->path = talloc_strdup(file, pos + 1);
+                       file->dev = talloc_strndup(file, str + 1,
(size_t) (pos - str - 1));
                }

You probably want to separate the missing-closing-bracket case from the
zero-length-path case here, since you're making the latter a valid
pathspec.

Regardless, in the zero-length path case, -> path is set to NULL, ->dev
is not set either, so this function still returns NULL. Are you sure
that this fixes the issue in the way that you expect?

Also, super minor, but: curly brackets and else on the same line, and
the last addition there is > 80 chars.

-
-               file->path = talloc_strdup(file, pos + 1);
-
-               dev_len = pos - str - 1;
-               if (dev_len)
-                       file->dev = talloc_strndup(file, str + 1,
dev_len);
        }
 
+       if (!file->dev && !file->path) {
+               talloc_free(file);
+               return NULL;
+       }

You've dropped the case where dev_len is zero here - ie., brackets are
present but empty. Then, since file->dev is a valid pointer to an empty
string, the pathspec "()" won't hit that last conditional.

That's not necessarily an issue, but we'd need to be aware of the
change.

Cheers,


Jeremy
Klaus Heinrich Kiwi Jan. 20, 2021, 5:32 p.m. UTC | #2
Hi Jeremy, thanks for the review

On 1/20/2021 2:03 AM, Jeremy Kerr wrote:
> Hi Klaus,
> 


> We will need to change the cases that assume ->path is non-null -
> currently, there are cases where this will result in a NULL 'b'
> argument to join_paths, which tries to strlen(b), and crashes.

I honestly did thought of that, but the (first) reference I found
on google was saying that strlen(null) would return zero, but
now looking again, I couldn't find that in a proper standard,
so I'll add the necessary checks.

It's funny, however, that it works (i.e., didn't crash for me on
the zero-length path scenario)


>                  return -1;
>   
>          *devp = dev;
> -       *pathp = talloc_strdup(script, file->path);
> +       *pathp = !file->path ? NULL : talloc_strdup(script, file-
>> path);> talloc_strdup() does the exactly this check, you don't need this
> change.

I guess I was being paranoid and trying to make it solar clear what
was happening, but will remove this (and other similar stuff)


> 
>   
>          } else {
>                  /* device plus path - split into components */
> @@ -137,17 +137,18 @@ struct grub2_file *grub2_parse_file(struct
> grub2_script *script,
>   
>                  /* no closing bracket, or zero-length path? */
>                  if (!pos || *(pos+1) == '\0') {
> -                       talloc_free(file);
> -                       return NULL;
> +                       file->path = NULL;
> +               }
> +               else {
> +                       file->path = talloc_strdup(file, pos + 1);
> +                       file->dev = talloc_strndup(file, str + 1,
> (size_t) (pos - str - 1));
>                  }
> 
> You probably want to separate the missing-closing-bracket case from the
> zero-length-path case here, since you're making the latter a valid
> pathspec.
> 
> Regardless, in the zero-length path case, -> path is set to NULL, ->dev
> is not set either, so this function still returns NULL. Are you sure
> that this fixes the issue in the way that you expect?

I'm not sure if '()/something' is legal, even less '()'. For consistency,
can we settle for both dev as well as path can EITHER be zero-length (but
not both at the same time)?

  
> Also, super minor, but: curly brackets and else on the same line, and
> the last addition there is > 80 chars.
>

oops! (guess Linus allowing up to 100 columns hasn't hit us yet here ;-)

> -
> -               file->path = talloc_strdup(file, pos + 1);
> -
> -               dev_len = pos - str - 1;
> -               if (dev_len)
> -                       file->dev = talloc_strndup(file, str + 1,
> dev_len);
>          }
>   
> +       if (!file->dev && !file->path) {
> +               talloc_free(file);
> +               return NULL;
> +       }
> 
> You've dropped the case where dev_len is zero here - ie., brackets are
> present but empty. Then, since file->dev is a valid pointer to an empty
> string, the pathspec "()" won't hit that last conditional.
> 
> That's not necessarily an issue, but we'd need to be aware of the
> change.

As commented above, I *think* we want to disallow that as it may mean
the script is probably broken, and we'd be masking it making it
difficult to know why.

> 
> Cheers,
> 
> 
> Jeremy
> 
> 

Thanks! I'll send a v3 shortly.
Jeremy Kerr Jan. 21, 2021, 12:50 a.m. UTC | #3
Hi Klaus,

> It's funny, however, that it works (i.e., didn't crash for me on
> the zero-length path scenario)

Yep, from reading your v1 patch, it looked like there was no way for
grub2_parse_file to return a NULL ->path, so I don't think you were
hitting that case.

> > Regardless, in the zero-length path case, -> path is set to NULL,
> > ->dev is not set either, so this function still returns NULL. Are
> > you sure that this fixes the issue in the way that you expect?
> 
> I'm not sure if '()/something' is legal, even less '()'. For
> consistency,

This is regarding an empty path specifier, not an empty device. It looks
like you've addressed that in v3 though.


> can we settle for both dev as well as path can EITHER be zero-length
> (but not both at the same time)?

Yep, I think that's a good approach.

Cheers,


Jeremy
diff mbox series

Patch

diff --git a/discover/grub2/builtins.c b/discover/grub2/builtins.c
index ab1407a..31cbe0e 100644
--- a/discover/grub2/builtins.c
+++ b/discover/grub2/builtins.c
@@ -216,7 +216,7 @@  static int parse_to_device_path(struct grub2_script *script,
 		return -1;
 
 	*devp = dev;
-	*pathp = talloc_strdup(script, file->path);
+	*pathp = !file->path ? NULL : talloc_strdup(script, file->path);
 
 	talloc_free(file);
 
@@ -247,13 +247,13 @@  static bool builtin_test_op_file(struct grub2_script *script, char op,
 	switch (op) {
 	case 's':
 		/* -s: return true if file exists and has non-zero size */
-		result = statbuf.st_size > 0;
+		result = !path ? false : statbuf.st_size > 0;
 		break;
 	case 'f':
 		/* -f: return true if file exists and is not a directory. This is
 		 * different than the behavior of "test", but is what GRUB does
 		 * (though note as above that we follow symlinks unlike GRUB). */
-		result = !S_ISDIR(statbuf.st_mode);
+		result = !path ? false : !S_ISDIR(statbuf.st_mode);
 		break;
 	default:
 		result = false;
@@ -284,7 +284,7 @@  static bool builtin_test_op_dir(struct grub2_script *script, char op,
 	if (rc)
 		return false;
 
-	return S_ISDIR(statbuf.st_mode);
+	return !path ? false : S_ISDIR(statbuf.st_mode);
 }
 
 static bool builtin_test_op(struct grub2_script *script,
diff --git a/discover/grub2/grub2.c b/discover/grub2/grub2.c
index b176ce2..52c75e9 100644
--- a/discover/grub2/grub2.c
+++ b/discover/grub2/grub2.c
@@ -118,7 +118,6 @@  struct grub2_file *grub2_parse_file(struct grub2_script *script,
 		const char *str)
 {
 	struct grub2_file *file;
-	size_t dev_len;
 	char *pos;
 
 	if (!str)
@@ -129,6 +128,7 @@  struct grub2_file *grub2_parse_file(struct grub2_script *script,
 	if (*str != '(') {
 		/* just a path - no device, return path as-is */
 		file->path = talloc_strdup(file, str);
+		file->dev = NULL;
 
 	} else {
 		/* device plus path - split into components */
@@ -137,17 +137,18 @@  struct grub2_file *grub2_parse_file(struct grub2_script *script,
 
 		/* no closing bracket, or zero-length path? */
 		if (!pos || *(pos+1) == '\0') {
-			talloc_free(file);
-			return NULL;
+			file->path = NULL;
+		}
+		else {
+			file->path = talloc_strdup(file, pos + 1);
+			file->dev = talloc_strndup(file, str + 1, (size_t) (pos - str - 1));
 		}
-
-		file->path = talloc_strdup(file, pos + 1);
-
-		dev_len = pos - str - 1;
-		if (dev_len)
-			file->dev = talloc_strndup(file, str + 1, dev_len);
 	}
 
+	if (!file->dev && !file->path) {
+		talloc_free(file);
+		return NULL;
+	}
 	return file;
 }
 
diff --git a/discover/paths.c b/discover/paths.c
index 16fdd59..3010ae3 100644
--- a/discover/paths.c
+++ b/discover/paths.c
@@ -55,9 +55,11 @@  char *join_paths(void *alloc_ctx, const char *a, const char *b)
 	full_path = talloc_array(alloc_ctx, char, strlen(a) + strlen(b) + 2);
 
 	strcpy(full_path, a);
-	if (b[0] != '/' && a[strlen(a) - 1] != '/')
-		strcat(full_path, "/");
-	strcat(full_path, b);
+	if (strlen(b)) {
+		if (b[0] != '/' && a[strlen(a) - 1] != '/')
+			strcat(full_path, "/");
+		strcat(full_path, b);
+	}
 
 	return full_path;
 }
diff --git a/test/parser/utils.c b/test/parser/utils.c
index d8499a4..2705b9a 100644
--- a/test/parser/utils.c
+++ b/test/parser/utils.c
@@ -257,7 +257,7 @@  int parser_stat_path(struct discover_context *ctx,
 	list_for_each_entry(&test->files, file, list) {
 		if (file->dev != dev)
 			continue;
-		if (strcmp(file->name, path))
+		if (path && strcmp(file->name, path))
 			continue;
 
 		statbuf->st_size = (off_t)file->size;