[image] Add the concept of a static image

Not all images are allocated via alloc_image().  For example: embedded
images, the static images created to hold a runtime command line, and
the images used by unit tests are all static structures.

Using image_set_cmdline() (via e.g. the "imgargs" command) to set the
command-line arguments of a static image will succeed but will leak
memory, since nothing will ever free the allocated command line.
There are no code paths that can lead to calling image_set_len() on a
static image, but there is no safety check against future code paths
attempting this.

Define a flag IMAGE_STATIC to mark an image as statically allocated,
generalise free_image() to also handle freeing dynamically allocated
portions of static images (such as the command line), and expose
free_image() for use by static images.

Define a related flag IMAGE_STATIC_NAME to mark the name as statically
allocated.  Allow a statically allocated name to be replaced with a
dynamically allocated name since this is a potentially valid use case
(e.g. if "imgdecrypt --name <name>" is used on an embedded image).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-04-30 13:22:54 +01:00
parent 3303910010
commit cd803ff2e2
9 changed files with 68 additions and 8 deletions

View File

@@ -70,6 +70,7 @@ static void cmdline_image_free ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt ); struct image *image = container_of ( refcnt, struct image, refcnt );
DBGC ( image, "RUNTIME freeing command line\n" ); DBGC ( image, "RUNTIME freeing command line\n" );
free_image ( refcnt );
free ( cmdline_copy ); free ( cmdline_copy );
} }
@@ -77,6 +78,7 @@ static void cmdline_image_free ( struct refcnt *refcnt ) {
static struct image cmdline_image = { static struct image cmdline_image = {
.refcnt = REF_INIT ( cmdline_image_free ), .refcnt = REF_INIT ( cmdline_image_free ),
.name = "<CMDLINE>", .name = "<CMDLINE>",
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ),
.type = &script_image_type, .type = &script_image_type,
}; };

View File

@@ -76,22 +76,41 @@ static int require_trusted_images_permanent = 0;
* Free executable image * Free executable image
* *
* @v refcnt Reference counter * @v refcnt Reference counter
*
* Image consumers must call image_put() rather than calling
* free_image() directly. This function is exposed for use only by
* static images.
*/ */
static void free_image ( struct refcnt *refcnt ) { void free_image ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt ); struct image *image = container_of ( refcnt, struct image, refcnt );
struct image_tag *tag; struct image_tag *tag;
/* Sanity check: free_image() should not be called directly on
* dynamically allocated images.
*/
assert ( refcnt->count < 0 );
DBGC ( image, "IMAGE %s freed\n", image->name ); DBGC ( image, "IMAGE %s freed\n", image->name );
/* Clear any tag weak references */
for_each_table_entry ( tag, IMAGE_TAGS ) { for_each_table_entry ( tag, IMAGE_TAGS ) {
if ( tag->image == image ) if ( tag->image == image )
tag->image = NULL; tag->image = NULL;
} }
free ( image->name );
/* Free dynamic allocations used by both static and dynamic images */
free ( image->cmdline ); free ( image->cmdline );
uri_put ( image->uri ); uri_put ( image->uri );
ufree ( image->data );
image_put ( image->replacement ); image_put ( image->replacement );
free ( image );
/* Free image name, if dynamically allocated */
if ( ! ( image->flags & IMAGE_STATIC_NAME ) )
free ( image->name );
/* Free image data and image itself, if dynamically allocated */
if ( ! ( image->flags & IMAGE_STATIC ) ) {
ufree ( image->data );
free ( image );
}
} }
/** /**
@@ -165,9 +184,13 @@ int image_set_name ( struct image *image, const char *name ) {
if ( ! name_copy ) if ( ! name_copy )
return -ENOMEM; return -ENOMEM;
/* Free existing name, if not statically allocated */
if ( ! ( image->flags & IMAGE_STATIC_NAME ) )
free ( image->name );
/* Replace existing name */ /* Replace existing name */
free ( image->name );
image->name = name_copy; image->name = name_copy;
image->flags &= ~IMAGE_STATIC_NAME;
return 0; return 0;
} }
@@ -220,6 +243,10 @@ int image_set_cmdline ( struct image *image, const char *cmdline ) {
int image_set_len ( struct image *image, size_t len ) { int image_set_len ( struct image *image, size_t len ) {
void *new; void *new;
/* Refuse to reallocate static images */
if ( image->flags & IMAGE_STATIC )
return -ENOTTY;
/* (Re)allocate image data */ /* (Re)allocate image data */
new = urealloc ( image->data, len ); new = urealloc ( image->data, len );
if ( ! new ) if ( ! new )
@@ -288,6 +315,13 @@ int register_image ( struct image *image ) {
char name[8]; /* "imgXXXX" */ char name[8]; /* "imgXXXX" */
int rc; int rc;
/* Sanity checks */
if ( image->flags & IMAGE_STATIC ) {
assert ( ( image->name == NULL ) ||
( image->flags & IMAGE_STATIC_NAME ) );
assert ( image->cmdline == NULL );
}
/* Create image name if it doesn't already have one */ /* Create image name if it doesn't already have one */
if ( ! image->name ) { if ( ! image->name ) {
snprintf ( name, sizeof ( name ), "img%d", imgindex++ ); snprintf ( name, sizeof ( name ), "img%d", imgindex++ );

View File

@@ -31,8 +31,9 @@ EMBED_ALL
/* Image structures for all embedded images */ /* Image structures for all embedded images */
#undef EMBED #undef EMBED
#define EMBED( _index, _path, _name ) { \ #define EMBED( _index, _path, _name ) { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( free_image ), \
.name = _name, \ .name = _name, \
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
.data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \ .data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \
.len = ( size_t ) embedded_image_ ## _index ## _len, \ .len = ( size_t ) embedded_image_ ## _index ## _len, \
}, },

View File

@@ -30,14 +30,22 @@ struct image {
/** URI of image */ /** URI of image */
struct uri *uri; struct uri *uri;
/** Name */ /** Name
*
* If the @c IMAGE_STATIC_NAME flag is set, then this is a
* statically allocated string.
*/
char *name; char *name;
/** Flags */ /** Flags */
unsigned int flags; unsigned int flags;
/** Command line to pass to image */ /** Command line to pass to image */
char *cmdline; char *cmdline;
/** Raw file image */ /** Raw file image
*
* If the @c IMAGE_STATIC flag is set, then this is a
* statically allocated image.
*/
void *data; void *data;
/** Length of raw file image */ /** Length of raw file image */
size_t len; size_t len;
@@ -72,6 +80,12 @@ struct image {
/** Image will be hidden from enumeration */ /** Image will be hidden from enumeration */
#define IMAGE_HIDDEN 0x0008 #define IMAGE_HIDDEN 0x0008
/** Image is statically allocated */
#define IMAGE_STATIC 0x0010
/** Image name is statically allocated */
#define IMAGE_STATIC_NAME 0x0020
/** An executable image type */ /** An executable image type */
struct image_type { struct image_type {
/** Name of this image type */ /** Name of this image type */
@@ -185,6 +199,7 @@ static inline struct image * first_image ( void ) {
return list_first_entry ( &images, struct image, list ); return list_first_entry ( &images, struct image, list );
} }
extern void free_image ( struct refcnt *refcnt );
extern struct image * alloc_image ( struct uri *uri ); extern struct image * alloc_image ( struct uri *uri );
extern int image_set_uri ( struct image *image, struct uri *uri ); extern int image_set_uri ( struct image *image, struct uri *uri );
extern int image_set_name ( struct image *image, const char *name ); extern int image_set_name ( struct image *image, const char *name );

View File

@@ -58,6 +58,7 @@ static void efi_cmdline_free ( struct refcnt *refcnt ) {
struct image *image = container_of ( refcnt, struct image, refcnt ); struct image *image = container_of ( refcnt, struct image, refcnt );
DBGC ( image, "CMDLINE freeing command line\n" ); DBGC ( image, "CMDLINE freeing command line\n" );
free_image ( refcnt );
free ( efi_cmdline_copy ); free ( efi_cmdline_copy );
} }
@@ -65,6 +66,7 @@ static void efi_cmdline_free ( struct refcnt *refcnt ) {
static struct image efi_cmdline_image = { static struct image efi_cmdline_image = {
.refcnt = REF_INIT ( efi_cmdline_free ), .refcnt = REF_INIT ( efi_cmdline_free ),
.name = "<CMDLINE>", .name = "<CMDLINE>",
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ),
.type = &script_image_type, .type = &script_image_type,
}; };

View File

@@ -46,6 +46,7 @@ struct asn1_test {
static struct image _name ## __image = { \ static struct image _name ## __image = { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( ref_no_free ), \
.name = #_name, \ .name = #_name, \
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
.data = ( userptr_t ) ( _name ## __file ), \ .data = ( userptr_t ) ( _name ## __file ), \
.len = sizeof ( _name ## __file ), \ .len = sizeof ( _name ## __file ), \
}; \ }; \

View File

@@ -85,6 +85,7 @@ struct cms_test_keypair {
.image = { \ .image = { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( ref_no_free ), \
.name = #NAME, \ .name = #NAME, \
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
.data = ( userptr_t ) ( NAME ## _data ), \ .data = ( userptr_t ) ( NAME ## _data ), \
.len = sizeof ( NAME ## _data ), \ .len = sizeof ( NAME ## _data ), \
}, \ }, \
@@ -97,6 +98,7 @@ struct cms_test_keypair {
.image = { \ .image = { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( ref_no_free ), \
.name = #NAME, \ .name = #NAME, \
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
.data = ( userptr_t ) ( NAME ## _data ), \ .data = ( userptr_t ) ( NAME ## _data ), \
.len = sizeof ( NAME ## _data ), \ .len = sizeof ( NAME ## _data ), \
}, \ }, \
@@ -109,6 +111,7 @@ struct cms_test_keypair {
.image = { \ .image = { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( ref_no_free ), \
.name = #NAME, \ .name = #NAME, \
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
.type = &der_image_type, \ .type = &der_image_type, \
.data = ( userptr_t ) ( NAME ## _data ), \ .data = ( userptr_t ) ( NAME ## _data ), \
.len = sizeof ( NAME ## _data ), \ .len = sizeof ( NAME ## _data ), \

View File

@@ -41,6 +41,7 @@ struct pixel_buffer_test {
static struct image _name ## __image = { \ static struct image _name ## __image = { \
.refcnt = REF_INIT ( ref_no_free ), \ .refcnt = REF_INIT ( ref_no_free ), \
.name = #_name, \ .name = #_name, \
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), \
.data = ( userptr_t ) ( _name ## __file ), \ .data = ( userptr_t ) ( _name ## __file ), \
.len = sizeof ( _name ## __file ), \ .len = sizeof ( _name ## __file ), \
}; \ }; \

View File

@@ -162,6 +162,7 @@ static struct image_type test_image_type = {
static struct image test_image = { static struct image test_image = {
.refcnt = REF_INIT ( ref_no_free ), .refcnt = REF_INIT ( ref_no_free ),
.name = "<TESTS>", .name = "<TESTS>",
.flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ),
.type = &test_image_type, .type = &test_image_type,
}; };