diff mbox series

[4/6] tools/libfsimage: Add an fdopen() interface to libfsimage

Message ID 20231106150508.22665-5-alejandro.vallejo@cloud.com (mailing list archive)
State New, archived
Headers show
Series Pygrub security enhancements and bugfixes | expand

Commit Message

Alejandro Vallejo Nov. 6, 2023, 3:05 p.m. UTC
Currently libfsimage requires taking a path so it can open a file itself.
This needs not to be the case and is, as far as I've gathered, a side
effect of the ext2fs-lib's optional dependency because it couldn't cope
directly with FDs until e2fsprogs-1.43.2. That was 2016, so it's safe to
make use of this feature in order to improve the security stance of pygrub.

  https://github.com/tytso/e2fsprogs/commit/4ccf9e4fe165cfa966c8af0f3d310230aa5c3a1e

This patch modifies the internals of libfsimage to:
  * Expose a new fdopen() function
    * Mirrors open(), except we pass an fd rather than a path
  * Refactor open() to use fdopen() internally
  * Rewire libfsimage so paths are not passed around
  * Rewire ext2fs-lib shim to use unixfd_io_manager

Note that while fdopen() takes an FD, close() will still close it. Clients
of libfsimage must ensure they duplicate any file descriptor they want to
preserve past the lifetime of the fsi_t* objects that use them.

Signed-off-by: Alejandro Vallejo <alejandro.vallejo@cloud.com>
---
 tools/libfsimage/common/fsimage.c           | 42 +++++++++++++++------
 tools/libfsimage/common/fsimage_grub.c      |  2 +-
 tools/libfsimage/common/fsimage_plugin.c    |  4 +-
 tools/libfsimage/common/fsimage_priv.h      |  3 +-
 tools/libfsimage/common/mapfile-GNU         |  2 +
 tools/libfsimage/common/mapfile-SunOS       |  2 +
 tools/libfsimage/common/xenfsimage.h        |  3 ++
 tools/libfsimage/common/xenfsimage_plugin.h |  2 +-
 tools/libfsimage/ext2fs-lib/ext2fs-lib.c    | 14 ++++++-
 9 files changed, 56 insertions(+), 18 deletions(-)

Comments

Andrew Cooper Nov. 22, 2023, 10:29 p.m. UTC | #1
On 06/11/2023 3:05 pm, Alejandro Vallejo wrote:
> diff --git a/tools/libfsimage/common/fsimage_priv.h b/tools/libfsimage/common/fsimage_priv.h
> index 2274403557..779e433b37 100644
> --- a/tools/libfsimage/common/fsimage_priv.h
> +++ b/tools/libfsimage/common/fsimage_priv.h
> @@ -29,6 +29,7 @@ extern C {
>  #endif
>  
>  #include <sys/types.h>
> +#include <stdbool.h>
>  
>  #include "xenfsimage.h"
>  #include "xenfsimage_plugin.h"
> @@ -54,7 +55,7 @@ struct fsi_file {
>  	void *ff_data;
>  };
>  
> -int find_plugin(fsi_t *, const char *, const char *);
> +int find_plugin(fsi_t *, const char *);
>  
>  #ifdef __cplusplus
>  };

These are the only two hunks in this file.  Is the stdbool include stale?

It seems to compile fine with it removed.

> diff --git a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
> index 864a15b349..9f07ea288f 100644
> --- a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
> +++ b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
> @@ -25,15 +25,25 @@
>  #include INCLUDE_EXTFS_H
>  #include <errno.h>
>  #include <inttypes.h>
> +#include <stdio.h>
>  
>  static int
> -ext2lib_mount(fsi_t *fsi, const char *name, const char *options)
> +ext2lib_mount(fsi_t *fsi, const char *options)
>  {
>  	int err;
>  	char opts[30] = "";
>  	ext2_filsys *fs;
>  	uint64_t offset = fsip_fs_offset(fsi);
>  
> +	/*
> +	 * We must choose unixfd_io_manager rather than unix_io_manager in
> +	 * order for the library to accept fd strings instead of paths. It
> +	 * still means we must pass a string representing an fd rather than
> +	 * an int, but at least this way we don't need to pass paths around
> +	 */
> +	char name[32] = {0};

For an int?  12 will do fine including a terminator, and practical
system limits leave it far smaller than that generally.

Given that it is guaranteed long enough, you don't need to zero it just
to have snprintf() write a well-formed string in.

~Andrew
Alejandro Vallejo Nov. 23, 2023, 4:59 p.m. UTC | #2
On 22/11/2023 22:29, Andrew Cooper wrote:
> On 06/11/2023 3:05 pm, Alejandro Vallejo wrote:
>> diff --git a/tools/libfsimage/common/fsimage_priv.h b/tools/libfsimage/common/fsimage_priv.h
>> index 2274403557..779e433b37 100644
>> --- a/tools/libfsimage/common/fsimage_priv.h
>> +++ b/tools/libfsimage/common/fsimage_priv.h
>> @@ -29,6 +29,7 @@ extern C {
>>   #endif
>>   
>>   #include <sys/types.h>
>> +#include <stdbool.h>
>>   
>>   #include "xenfsimage.h"
>>   #include "xenfsimage_plugin.h"
>> @@ -54,7 +55,7 @@ struct fsi_file {
>>   	void *ff_data;
>>   };
>>   
>> -int find_plugin(fsi_t *, const char *, const char *);
>> +int find_plugin(fsi_t *, const char *);
>>   
>>   #ifdef __cplusplus
>>   };
> 
> These are the only two hunks in this file.  Is the stdbool include stale?
> 
> It seems to compile fine with it removed.
I think it's leftover from code I needed initially and then didn't 
anymore. Safe to get rid of.

> 
>> diff --git a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
>> index 864a15b349..9f07ea288f 100644
>> --- a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
>> +++ b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
>> @@ -25,15 +25,25 @@
>>   #include INCLUDE_EXTFS_H
>>   #include <errno.h>
>>   #include <inttypes.h>
>> +#include <stdio.h>
>>   
>>   static int
>> -ext2lib_mount(fsi_t *fsi, const char *name, const char *options)
>> +ext2lib_mount(fsi_t *fsi, const char *options)
>>   {
>>   	int err;
>>   	char opts[30] = "";
>>   	ext2_filsys *fs;
>>   	uint64_t offset = fsip_fs_offset(fsi);
>>   
>> +	/*
>> +	 * We must choose unixfd_io_manager rather than unix_io_manager in
>> +	 * order for the library to accept fd strings instead of paths. It
>> +	 * still means we must pass a string representing an fd rather than
>> +	 * an int, but at least this way we don't need to pass paths around
>> +	 */
>> +	char name[32] = {0};
> 
> For an int?  12 will do fine including a terminator, and practical
> system limits leave it far smaller than that generally.
Maybe. I admit optimising that line went pretty low on the list of 
things I cared terribly about. I'm happy to reduce it, but it is 
inconsequential.
> 
> Given that it is guaranteed long enough, you don't need to zero it just
> to have snprintf() write a well-formed string in.
> 
> ~Andrew
For wellformed fd's that's true. Through bugs or malice that may not be 
the case. I'm happier knowing at least the last zero remains.

Cheers,
Alejandro
diff mbox series

Patch

diff --git a/tools/libfsimage/common/fsimage.c b/tools/libfsimage/common/fsimage.c
index 5cfa56a84d..1fd302e506 100644
--- a/tools/libfsimage/common/fsimage.c
+++ b/tools/libfsimage/common/fsimage.c
@@ -38,15 +38,32 @@  static pthread_mutex_t fsi_lock = PTHREAD_MUTEX_INITIALIZER;
 
 fsi_t *fsi_open_fsimage(const char *path, uint64_t off, const char *options)
 {
-	fsi_t *fsi = NULL;
+	fsi_t *fsi;
 	int fd;
 	int err;
 
 	if ((fd = open(path, O_RDONLY)) == -1)
-		goto fail;
+		return NULL;
+
+	fsi = fsi_fdopen_fsimage(fd, off, options);
+
+	if (fsi)
+		return fsi;
+
+	err = errno;
+	(void) close(fd);
+	errno = err;
+
+	return NULL;
+}
+
+fsi_t *fsi_fdopen_fsimage(int fd, uint64_t off, const char *options)
+{
+	fsi_t *fsi = NULL;
+	int err;
 
 	if ((fsi = malloc(sizeof(*fsi))) == NULL)
-		goto fail;
+		return NULL;
 
 	fsi->f_fd = fd;
 	fsi->f_off = off;
@@ -54,20 +71,17 @@  fsi_t *fsi_open_fsimage(const char *path, uint64_t off, const char *options)
 	fsi->f_bootstring = NULL;
 
 	pthread_mutex_lock(&fsi_lock);
-	err = find_plugin(fsi, path, options);
+	err = find_plugin(fsi, options);
 	pthread_mutex_unlock(&fsi_lock);
-	if (err != 0)
-		goto fail;
 
-	return (fsi);
+	if (!err)
+		return fsi;
 
-fail:
 	err = errno;
-	if (fd != -1)
-		(void) close(fd);
 	free(fsi);
 	errno = err;
-	return (NULL);
+
+	return NULL;
 }
 
 void fsi_close_fsimage(fsi_t *fsi)
@@ -167,3 +181,9 @@  fsi_fs_bootstring(fsi_t *fsi)
 {
 	return (fsi->f_bootstring);
 }
+
+int
+fsi_fd(fsi_t *fsi)
+{
+	return (fsi->f_fd);
+}
diff --git a/tools/libfsimage/common/fsimage_grub.c b/tools/libfsimage/common/fsimage_grub.c
index 258d48bfbb..04397235f7 100644
--- a/tools/libfsimage/common/fsimage_grub.c
+++ b/tools/libfsimage/common/fsimage_grub.c
@@ -221,7 +221,7 @@  fsig_substring(const char *s1, const char *s2)
 }
 
 static int
-fsig_mount(fsi_t *fsi, const char *path, const char *options)
+fsig_mount(fsi_t *fsi, const char *options)
 {
 	fsig_plugin_ops_t *ops = fsi->f_plugin->fp_data;
 	fsi_file_t *ffi;
diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c
index d0cb9e96a6..6645ce3bfe 100644
--- a/tools/libfsimage/common/fsimage_plugin.c
+++ b/tools/libfsimage/common/fsimage_plugin.c
@@ -175,7 +175,7 @@  fail:
 	return (ret);
 }
 
-int find_plugin(fsi_t *fsi, const char *path, const char *options)
+int find_plugin(fsi_t *fsi, const char *options)
 {
 	fsi_plugin_t *fp;
 	int ret = 0;
@@ -185,7 +185,7 @@  int find_plugin(fsi_t *fsi, const char *path, const char *options)
 
 	for (fp = plugins; fp != NULL; fp = fp->fp_next) {
 		fsi->f_plugin = fp;
-		if (fp->fp_ops->fpo_mount(fsi, path, options) == 0)
+		if (fp->fp_ops->fpo_mount(fsi, options) == 0)
 			goto out;
 	}
 
diff --git a/tools/libfsimage/common/fsimage_priv.h b/tools/libfsimage/common/fsimage_priv.h
index 2274403557..779e433b37 100644
--- a/tools/libfsimage/common/fsimage_priv.h
+++ b/tools/libfsimage/common/fsimage_priv.h
@@ -29,6 +29,7 @@  extern C {
 #endif
 
 #include <sys/types.h>
+#include <stdbool.h>
 
 #include "xenfsimage.h"
 #include "xenfsimage_plugin.h"
@@ -54,7 +55,7 @@  struct fsi_file {
 	void *ff_data;
 };
 
-int find_plugin(fsi_t *, const char *, const char *);
+int find_plugin(fsi_t *, const char *);
 
 #ifdef __cplusplus
 };
diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU
index 2d54d527d7..658f313315 100644
--- a/tools/libfsimage/common/mapfile-GNU
+++ b/tools/libfsimage/common/mapfile-GNU
@@ -3,6 +3,7 @@  VERSION {
 		global:
 			fsi_init;
 			fsi_open_fsimage;
+			fsi_fdopen_fsimage;
 			fsi_close_fsimage;
 			fsi_file_exists;
 			fsi_open_file;
@@ -12,6 +13,7 @@  VERSION {
 			fsi_bootstring_alloc;
 			fsi_bootstring_free;
 			fsi_fs_bootstring;
+			fsi_fd;
 	
 			fsip_fs_set_data;
 			fsip_file_alloc;
diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS
index 48deedb425..a03646ff73 100644
--- a/tools/libfsimage/common/mapfile-SunOS
+++ b/tools/libfsimage/common/mapfile-SunOS
@@ -2,6 +2,7 @@  libfsimage.so.1.0 {
 	global:
 		fsi_init;
 		fsi_open_fsimage;
+		fsi_fdopen_fsimage;
 		fsi_close_fsimage;
 		fsi_file_exists;
 		fsi_open_file;
@@ -11,6 +12,7 @@  libfsimage.so.1.0 {
 		fsi_bootstring_alloc;
 		fsi_bootstring_free;
 		fsi_fs_bootstring;
+		fsi_fd;
 
 		fsip_fs_set_data;
 		fsip_file_alloc;
diff --git a/tools/libfsimage/common/xenfsimage.h b/tools/libfsimage/common/xenfsimage.h
index 341883b2d7..8048cc7470 100644
--- a/tools/libfsimage/common/xenfsimage.h
+++ b/tools/libfsimage/common/xenfsimage.h
@@ -44,6 +44,7 @@  typedef struct fsi_file fsi_file_t;
 int fsi_init(void);
 
 fsi_t *fsi_open_fsimage(const char *, uint64_t, const char *);
+fsi_t *fsi_fdopen_fsimage(int, uint64_t, const char *);
 void fsi_close_fsimage(fsi_t *);
 
 int fsi_file_exists(fsi_t *, const char *);
@@ -57,6 +58,8 @@  char *fsi_bootstring_alloc(fsi_t *, size_t);
 void fsi_bootstring_free(fsi_t *);
 char *fsi_fs_bootstring(fsi_t *);
 
+int fsi_fd(fsi_t *);
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/tools/libfsimage/common/xenfsimage_plugin.h b/tools/libfsimage/common/xenfsimage_plugin.h
index 4135769018..996ea5ecbb 100644
--- a/tools/libfsimage/common/xenfsimage_plugin.h
+++ b/tools/libfsimage/common/xenfsimage_plugin.h
@@ -38,7 +38,7 @@  typedef struct fsi_plugin fsi_plugin_t;
 
 typedef struct fsi_plugin_ops {
 	int fpo_version;
-	int (*fpo_mount)(fsi_t *, const char *, const char *);
+	int (*fpo_mount)(fsi_t *, const char *);
 	int (*fpo_umount)(fsi_t *);
 	fsi_file_t *(*fpo_open)(fsi_t *, const char *);
 	ssize_t (*fpo_read)(fsi_file_t *, void *, size_t);
diff --git a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
index 864a15b349..9f07ea288f 100644
--- a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
+++ b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
@@ -25,15 +25,25 @@ 
 #include INCLUDE_EXTFS_H
 #include <errno.h>
 #include <inttypes.h>
+#include <stdio.h>
 
 static int
-ext2lib_mount(fsi_t *fsi, const char *name, const char *options)
+ext2lib_mount(fsi_t *fsi, const char *options)
 {
 	int err;
 	char opts[30] = "";
 	ext2_filsys *fs;
 	uint64_t offset = fsip_fs_offset(fsi);
 
+	/*
+	 * We must choose unixfd_io_manager rather than unix_io_manager in
+	 * order for the library to accept fd strings instead of paths. It
+	 * still means we must pass a string representing an fd rather than
+	 * an int, but at least this way we don't need to pass paths around
+	 */
+	char name[32] = {0};
+	(void)snprintf(name, sizeof(name) - 1, "%d", fsi_fd(fsi));
+
 	if (offset)
 		snprintf(opts, 29, "offset=%" PRId64, offset);
 
@@ -41,7 +51,7 @@  ext2lib_mount(fsi_t *fsi, const char *name, const char *options)
 	if (fs == NULL)
 		return (-1);
 
-	err = ext2fs_open2(name, opts, 0, 0, 0, unix_io_manager, fs);
+	err = ext2fs_open2(name, opts, 0, 0, 0, unixfd_io_manager, fs);
 
 	if (err != 0) {
 		free(fs);