* [Qemu-devel] [PATCH v5 1/5] job: Fix off-by-one assert checks for JobSTT and JobVerbTable
2018-11-05 21:38 [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Liam Merwick
@ 2018-11-05 21:38 ` Liam Merwick
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 2/5] block: Null pointer dereference in blk_root_get_parent_desc() Liam Merwick
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Liam Merwick @ 2018-11-05 21:38 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange, mreitz
In the assert checking the array dereference of JobVerbTable[verb]
in job_apply_verb() the check of the index, verb, allows an overrun
because an index equal to the array size is permitted.
Similarly, in the assert check of JobSTT[s0][s1] with index s1
in job_state_transition(), an off-by-one overrun is not flagged
either.
This is not a run-time issue as there are no callers actually
passing in the max value.
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
Reviewed-by: Darren Kenny <Darren.Kenny@oracle.com>
Reviewed-by: Mark Kanda <Mark.Kanda@oracle.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
---
job.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/job.c b/job.c
index c65e01bbfa34..da8e4b7bf2f3 100644
--- a/job.c
+++ b/job.c
@@ -159,7 +159,7 @@ bool job_is_internal(Job *job)
static void job_state_transition(Job *job, JobStatus s1)
{
JobStatus s0 = job->status;
- assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
+ assert(s1 >= 0 && s1 < JOB_STATUS__MAX);
trace_job_state_transition(job, job->ret,
JobSTT[s0][s1] ? "allowed" : "disallowed",
JobStatus_str(s0), JobStatus_str(s1));
@@ -174,7 +174,7 @@ static void job_state_transition(Job *job, JobStatus s1)
int job_apply_verb(Job *job, JobVerb verb, Error **errp)
{
JobStatus s0 = job->status;
- assert(verb >= 0 && verb <= JOB_VERB__MAX);
+ assert(verb >= 0 && verb < JOB_VERB__MAX);
trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
JobVerbTable[verb][s0] ? "allowed" : "prohibited");
if (JobVerbTable[verb][s0]) {
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH v5 2/5] block: Null pointer dereference in blk_root_get_parent_desc()
2018-11-05 21:38 [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Liam Merwick
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 1/5] job: Fix off-by-one assert checks for JobSTT and JobVerbTable Liam Merwick
@ 2018-11-05 21:38 ` Liam Merwick
2018-11-11 19:12 ` Max Reitz
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 3/5] qemu-img: assert block_job_get() does not return NULL in img_commit() Liam Merwick
` (3 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Liam Merwick @ 2018-11-05 21:38 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange, mreitz
The dev_id returned by the call to blk_get_attached_dev_id() in
blk_root_get_parent_desc() can be NULL (an internal call to
object_get_canonical_path may have returned NULL).
Instead of just checking this case before before dereferencing,
adjust blk_get_attached_dev_id() to return the empty string if no
object path can be found (similar to the case when blk->dev is NULL
and an empty string is returned).
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
---
block/block-backend.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/block/block-backend.c b/block/block-backend.c
index dc0cd5772413..a2061a565024 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -918,7 +918,8 @@ char *blk_get_attached_dev_id(BlockBackend *blk)
} else if (dev->id) {
return g_strdup(dev->id);
}
- return object_get_canonical_path(OBJECT(dev));
+
+ return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
}
/*
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/5] block: Null pointer dereference in blk_root_get_parent_desc()
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 2/5] block: Null pointer dereference in blk_root_get_parent_desc() Liam Merwick
@ 2018-11-11 19:12 ` Max Reitz
0 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2018-11-11 19:12 UTC (permalink / raw)
To: Liam Merwick, qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange
[-- Attachment #1: Type: text/plain, Size: 673 bytes --]
On 05.11.18 22:38, Liam Merwick wrote:
> The dev_id returned by the call to blk_get_attached_dev_id() in
> blk_root_get_parent_desc() can be NULL (an internal call to
> object_get_canonical_path may have returned NULL).
>
> Instead of just checking this case before before dereferencing,
> adjust blk_get_attached_dev_id() to return the empty string if no
> object path can be found (similar to the case when blk->dev is NULL
> and an empty string is returned).
>
> Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
> ---
> block/block-backend.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
Reviewed-by: Max Reitz <mreitz@redhat.com>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH v5 3/5] qemu-img: assert block_job_get() does not return NULL in img_commit()
2018-11-05 21:38 [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Liam Merwick
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 1/5] job: Fix off-by-one assert checks for JobSTT and JobVerbTable Liam Merwick
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 2/5] block: Null pointer dereference in blk_root_get_parent_desc() Liam Merwick
@ 2018-11-05 21:38 ` Liam Merwick
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 4/5] block: Fix potential Null pointer dereferences in vvfat.c Liam Merwick
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Liam Merwick @ 2018-11-05 21:38 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange, mreitz
Although the function block_job_get() can return NULL, it would be a
serious bug if it did so (because the job yields before executing anything
(if it started successfully); but otherwise, commit_active_start() would
have returned an error). However, as a precaution, before dereferencing
the 'job' pointer in img_commit() assert it is not NULL.
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
qemu-img.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/qemu-img.c b/qemu-img.c
index b12f4cd19b0a..457aa152296b 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1029,6 +1029,7 @@ static int img_commit(int argc, char **argv)
}
job = block_job_get("commit");
+ assert(job);
run_block_job(job, &local_err);
if (local_err) {
goto unref_backing;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH v5 4/5] block: Fix potential Null pointer dereferences in vvfat.c
2018-11-05 21:38 [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Liam Merwick
` (2 preceding siblings ...)
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 3/5] qemu-img: assert block_job_get() does not return NULL in img_commit() Liam Merwick
@ 2018-11-05 21:38 ` Liam Merwick
2018-11-11 19:22 ` Max Reitz
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 5/5] qcow2: Read outside array bounds in qcow2_pre_write_overlap_check() Liam Merwick
2018-11-11 19:31 ` [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Max Reitz
5 siblings, 1 reply; 9+ messages in thread
From: Liam Merwick @ 2018-11-05 21:38 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange, mreitz
The calls to find_mapping_for_cluster() may return NULL but it
isn't always checked for before dereferencing the value returned.
Additionally, add some asserts to cover cases where NULL can't
be returned but which might not be obvious at first glance.
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
---
block/vvfat.c | 50 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 34 insertions(+), 16 deletions(-)
diff --git a/block/vvfat.c b/block/vvfat.c
index fc41841a5c3c..263274d9739a 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -100,30 +100,26 @@ static inline void array_free(array_t* array)
/* does not automatically grow */
static inline void* array_get(array_t* array,unsigned int index) {
assert(index < array->next);
+ assert(array->pointer);
return array->pointer + index * array->item_size;
}
-static inline int array_ensure_allocated(array_t* array, int index)
+static inline void array_ensure_allocated(array_t *array, int index)
{
if((index + 1) * array->item_size > array->size) {
int new_size = (index + 32) * array->item_size;
array->pointer = g_realloc(array->pointer, new_size);
- if (!array->pointer)
- return -1;
+ assert(array->pointer);
memset(array->pointer + array->size, 0, new_size - array->size);
array->size = new_size;
array->next = index + 1;
}
-
- return 0;
}
static inline void* array_get_next(array_t* array) {
unsigned int next = array->next;
- if (array_ensure_allocated(array, next) < 0)
- return NULL;
-
+ array_ensure_allocated(array, next);
array->next = next + 1;
return array_get(array, next);
}
@@ -2428,16 +2424,13 @@ static int commit_direntries(BDRVVVFATState* s,
direntry_t* direntry = array_get(&(s->directory), dir_index);
uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
-
int factor = 0x10 * s->sectors_per_cluster;
int old_cluster_count, new_cluster_count;
- int current_dir_index = mapping->info.dir.first_dir_index;
- int first_dir_index = current_dir_index;
+ int current_dir_index;
+ int first_dir_index;
int ret, i;
uint32_t c;
-DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
-
assert(direntry);
assert(mapping);
assert(mapping->begin == first_cluster);
@@ -2445,6 +2438,15 @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp
assert(mapping->mode & MODE_DIRECTORY);
assert(dir_index == 0 || is_directory(direntry));
+ if (mapping == NULL) {
+ return -1;
+ }
+
+DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n",
+ mapping->path, parent_mapping_index));
+
+ current_dir_index = mapping->info.dir.first_dir_index;
+ first_dir_index = current_dir_index;
mapping->info.dir.parent_mapping_index = parent_mapping_index;
if (first_cluster == 0) {
@@ -2494,6 +2496,9 @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp
direntry = array_get(&(s->directory), first_dir_index + i);
if (is_directory(direntry) && !is_dot(direntry)) {
mapping = find_mapping_for_cluster(s, first_cluster);
+ if (mapping == NULL) {
+ return -1;
+ }
assert(mapping->mode & MODE_DIRECTORY);
ret = commit_direntries(s, first_dir_index + i,
array_index(&(s->mapping), mapping));
@@ -2522,6 +2527,10 @@ static int commit_one_file(BDRVVVFATState* s,
assert(offset < size);
assert((offset % s->cluster_size) == 0);
+ if (mapping == NULL) {
+ return -1;
+ }
+
for (i = s->cluster_size; i < offset; i += s->cluster_size)
c = modified_fat_get(s, c);
@@ -2668,8 +2677,12 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
if (commit->action == ACTION_RENAME) {
mapping_t* mapping = find_mapping_for_cluster(s,
commit->param.rename.cluster);
- char* old_path = mapping->path;
+ char *old_path;
+ if (mapping == NULL) {
+ return -1;
+ }
+ old_path = mapping->path;
assert(commit->path);
mapping->path = commit->path;
if (rename(old_path, mapping->path))
@@ -2690,10 +2703,15 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
direntry_t* d = direntry + i;
if (is_file(d) || (is_directory(d) && !is_dot(d))) {
+ int l;
+ char *new_path;
mapping_t* m = find_mapping_for_cluster(s,
begin_of_direntry(d));
- int l = strlen(m->path);
- char* new_path = g_malloc(l + diff + 1);
+ if (m == NULL) {
+ return -1;
+ }
+ l = strlen(m->path);
+ new_path = g_malloc(l + diff + 1);
assert(!strncmp(m->path, mapping->path, l2));
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v5 4/5] block: Fix potential Null pointer dereferences in vvfat.c
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 4/5] block: Fix potential Null pointer dereferences in vvfat.c Liam Merwick
@ 2018-11-11 19:22 ` Max Reitz
0 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2018-11-11 19:22 UTC (permalink / raw)
To: Liam Merwick, qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange
[-- Attachment #1: Type: text/plain, Size: 2870 bytes --]
On 05.11.18 22:38, Liam Merwick wrote:
> The calls to find_mapping_for_cluster() may return NULL but it
> isn't always checked for before dereferencing the value returned.
> Additionally, add some asserts to cover cases where NULL can't
> be returned but which might not be obvious at first glance.
>
> Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
> ---
> block/vvfat.c | 50 ++++++++++++++++++++++++++++++++++----------------
> 1 file changed, 34 insertions(+), 16 deletions(-)
>
> diff --git a/block/vvfat.c b/block/vvfat.c
> index fc41841a5c3c..263274d9739a 100644
> --- a/block/vvfat.c
> +++ b/block/vvfat.c
[...]
> @@ -2428,16 +2424,13 @@ static int commit_direntries(BDRVVVFATState* s,
> direntry_t* direntry = array_get(&(s->directory), dir_index);
> uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
> mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
> -
> int factor = 0x10 * s->sectors_per_cluster;
> int old_cluster_count, new_cluster_count;
> - int current_dir_index = mapping->info.dir.first_dir_index;
> - int first_dir_index = current_dir_index;
> + int current_dir_index;
> + int first_dir_index;
> int ret, i;
> uint32_t c;
>
> -DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
> -
> assert(direntry);
> assert(mapping);
Oh, having moved the condition below the declarations brings an
interesting point to light, which is that there is an assertion for it
here already. So...
> assert(mapping->begin == first_cluster);
> @@ -2445,6 +2438,15 @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp
> assert(mapping->mode & MODE_DIRECTORY);
> assert(dir_index == 0 || is_directory(direntry));
>
> + if (mapping == NULL) {
> + return -1;
> + }
> +
...this should just not be added altogether.
> +DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n",
> + mapping->path, parent_mapping_index));
Moving this and the following dereferencing statements below that
assertion is reasonable, though. I think you should indent the DLOG()
while you're at it, though, because there is no reason not to, and the
way it is here just violates the coding style. (Disregarding that
vvfat.c effectively is a complete violation of the qemu coding style.
*cough*)
> +
> + current_dir_index = mapping->info.dir.first_dir_index;
> + first_dir_index = current_dir_index;
> mapping->info.dir.parent_mapping_index = parent_mapping_index;
>
> if (first_cluster == 0) {
So with the "if (mapping == NULL) {}" block above (hunk @@2445) dropped
and the DLOG() indented:
Reviewed-by: Max Reitz <mreitz@redhat.com>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH v5 5/5] qcow2: Read outside array bounds in qcow2_pre_write_overlap_check()
2018-11-05 21:38 [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Liam Merwick
` (3 preceding siblings ...)
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 4/5] block: Fix potential Null pointer dereferences in vvfat.c Liam Merwick
@ 2018-11-05 21:38 ` Liam Merwick
2018-11-11 19:31 ` [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Max Reitz
5 siblings, 0 replies; 9+ messages in thread
From: Liam Merwick @ 2018-11-05 21:38 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange, mreitz
The commit for 0e4e4318eaa5 increments QCOW2_OL_MAX_BITNR but does not
add an array entry for QCOW2_OL_BITMAP_DIRECTORY_BITNR to metadata_ol_names[].
As a result, an array dereference of metadata_ol_names[8] in
qcow2_pre_write_overlap_check() could result in a read outside of the array bounds.
Fixes: 0e4e4318eaa5 ('qcow2: add overlap check for bitmap directory')
Cc: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
block/qcow2-refcount.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 3c539f02e5ec..46082aeac1d6 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -2719,15 +2719,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
}
static const char *metadata_ol_names[] = {
- [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
- [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
- [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
- [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
- [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
- [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
- [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
- [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
+ [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
+ [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
+ [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
+ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
+ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
+ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
+ [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
+ [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
+ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory",
};
+QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
/*
* First performs a check for metadata overlaps (through
--
1.8.3.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis
2018-11-05 21:38 [Qemu-devel] [PATCH v5 0/5] off-by-one and NULL pointer accesses detected by static analysis Liam Merwick
` (4 preceding siblings ...)
2018-11-05 21:38 ` [Qemu-devel] [PATCH v5 5/5] qcow2: Read outside array bounds in qcow2_pre_write_overlap_check() Liam Merwick
@ 2018-11-11 19:31 ` Max Reitz
5 siblings, 0 replies; 9+ messages in thread
From: Max Reitz @ 2018-11-11 19:31 UTC (permalink / raw)
To: Liam Merwick, qemu-devel; +Cc: qemu-block, kwolf, jsnow, berrange
[-- Attachment #1: Type: text/plain, Size: 529 bytes --]
On 05.11.18 22:38, Liam Merwick wrote:
> Below are a number of fixes to some off-by-one, read outside array bounds, and
> NULL pointer accesses detected by an internal Oracle static analysis tool (Parfait).
> https://labs.oracle.com/pls/apex/f?p=labs:49:::::P49_PROJECT_ID:13
I decided to just fix the issue I had in patch 4 (dropped the "if" block
that was not doing much, and fixed the DLOG() indentation) and applied
the patch to my block branch:
https://git.xanclic.moe/XanClic/qemu/commits/branch/block
Max
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread