Message ID | 20220907153513.GA147130@ubuntu |
---|---|
State | New |
Headers | show |
Series | [v4] efi: capsule-loader: Fix use-after-free in efi_capsule_write | expand |
On Wed, 7 Sept 2022 at 17:35, Hyunwoo Kim <imv4bel@gmail.com> wrote: > > A race condition may occur if the user calls close() on another > thread during a write() operation on the device node of the efi capsule. > > This is a race condition that occurs between the efi_capsule_write() > and efi_capsule_flush() functions of efi_capsule_fops, > which ultimately results in UAF. > > So, the page freeing process is modified to be done in > efi_capsule_release() instead of efi_capsule_flush(). > > Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com> > --- > drivers/firmware/efi/capsule-loader.c | 29 ++++++--------------------- > 1 file changed, 6 insertions(+), 23 deletions(-) > > diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c > index 4dde8edd53b6..788e860304ae 100644 > --- a/drivers/firmware/efi/capsule-loader.c > +++ b/drivers/firmware/efi/capsule-loader.c > @@ -243,18 +243,17 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, > } > > /** > - * efi_capsule_flush - called by file close or file flush > + * efi_capsule_release - called by file close > + * @inode: not used > * @file: file pointer > - * @id: not used > * > - * If a capsule is being partially uploaded then calling this function > - * will be treated as upload termination and will free those completed > - * buffer pages and -ECANCELED will be returned. > + * We will not free successfully submitted pages since efi update > + * requires data to be maintained across system reboot. > **/ > -static int efi_capsule_flush(struct file *file, fl_owner_t id) > +static int efi_capsule_release(struct inode *inode, struct file *file) > { > - int ret = 0; > struct capsule_info *cap_info = file->private_data; > + int ret = 0; > > if (cap_info->index > 0) { OK, so what happens if the /dev/efi_capsule_loader is close() after a successful write and capsule update? AIUI, the pages should not be freed in that case, as the firmware will perform the capsule update based on their contents. So I assume this condition should become something like if (cap_info->index > 0 && (cap_info->header.headersize == 0 || cap_info->count < cap_info->total_size)) { so the inverse condition of what triggers the actual call to efi_capsule_submit_update() is used to decide whether the capsule update happened, and whether the pages need to be preserved. > pr_err("capsule upload not complete\n"); > @@ -262,21 +261,6 @@ static int efi_capsule_flush(struct file *file, fl_owner_t id) > ret = -ECANCELED; The return value of the release() hook doesn't seem to be used anywhere so I'm not sure this is worth keeping. > } > > - return ret; > -} > - > -/** > - * efi_capsule_release - called by file close > - * @inode: not used > - * @file: file pointer > - * > - * We will not free successfully submitted pages since efi update > - * requires data to be maintained across system reboot. > - **/ > -static int efi_capsule_release(struct inode *inode, struct file *file) > -{ > - struct capsule_info *cap_info = file->private_data; > - > kfree(cap_info->pages); > kfree(cap_info->phys); > kfree(file->private_data); > @@ -324,7 +308,6 @@ static const struct file_operations efi_capsule_fops = { > .owner = THIS_MODULE, > .open = efi_capsule_open, > .write = efi_capsule_write, > - .flush = efi_capsule_flush, > .release = efi_capsule_release, > .llseek = no_llseek, > }; > -- > 2.25.1 >
diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index 4dde8edd53b6..788e860304ae 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -243,18 +243,17 @@ static ssize_t efi_capsule_write(struct file *file, const char __user *buff, } /** - * efi_capsule_flush - called by file close or file flush + * efi_capsule_release - called by file close + * @inode: not used * @file: file pointer - * @id: not used * - * If a capsule is being partially uploaded then calling this function - * will be treated as upload termination and will free those completed - * buffer pages and -ECANCELED will be returned. + * We will not free successfully submitted pages since efi update + * requires data to be maintained across system reboot. **/ -static int efi_capsule_flush(struct file *file, fl_owner_t id) +static int efi_capsule_release(struct inode *inode, struct file *file) { - int ret = 0; struct capsule_info *cap_info = file->private_data; + int ret = 0; if (cap_info->index > 0) { pr_err("capsule upload not complete\n"); @@ -262,21 +261,6 @@ static int efi_capsule_flush(struct file *file, fl_owner_t id) ret = -ECANCELED; } - return ret; -} - -/** - * efi_capsule_release - called by file close - * @inode: not used - * @file: file pointer - * - * We will not free successfully submitted pages since efi update - * requires data to be maintained across system reboot. - **/ -static int efi_capsule_release(struct inode *inode, struct file *file) -{ - struct capsule_info *cap_info = file->private_data; - kfree(cap_info->pages); kfree(cap_info->phys); kfree(file->private_data); @@ -324,7 +308,6 @@ static const struct file_operations efi_capsule_fops = { .owner = THIS_MODULE, .open = efi_capsule_open, .write = efi_capsule_write, - .flush = efi_capsule_flush, .release = efi_capsule_release, .llseek = no_llseek, };
A race condition may occur if the user calls close() on another thread during a write() operation on the device node of the efi capsule. This is a race condition that occurs between the efi_capsule_write() and efi_capsule_flush() functions of efi_capsule_fops, which ultimately results in UAF. So, the page freeing process is modified to be done in efi_capsule_release() instead of efi_capsule_flush(). Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com> --- drivers/firmware/efi/capsule-loader.c | 29 ++++++--------------------- 1 file changed, 6 insertions(+), 23 deletions(-)