Repository navigation
DAOS-19697 client: accept labels that start with a UUID - #19154
Conversation
daos_is_valid_uuid_string() checked strnlen(uuid, 36) == 36, which is true for any string of at least 36 characters, so a label such as <uuid>-clone was taken for a UUID and rejected by daos_label_is_valid(). Read one character past 36 so only an exact 36-character string passes. Signed-off-by: Or Hayat <or.hayat@volumez.com>
|
Ticket title is 'daos_label_is_valid() rejects labels that start with a UUID' |
Update the daos_types.h copyright year and apply clang-format to the valid_labels array the change touched. Signed-off-by: Or Hayat <or.hayat@volumez.com>
Restore the array's original layout and exclude it from clang-format, which split the max-length literal and broke the clang build. Signed-off-by: Or Hayat <or.hayat@volumez.com>
|
hi i fixed the clang format issue and the compilation issue that it caused. |
| int i; | ||
|
|
||
| if (strnlen(uuid, len) != len) | ||
| if (strnlen(uuid, len + 1) != len) |
There was a problem hiding this comment.
The fix is correct for labels, but it changes the shared helper requirement: it now reads byte 36, so it assumes the input is NULL terminated.
The checker callers (chk_pools_pause_cb, chk_pools_cleanup_cb, chk_pools_load_from_db) pass raw VOS akey buffers that were stored with strlen(uuid_str). That's exactly 36 bytes with no terminator, so byte 36 is outside the key. @Nasf-Fan any thoughts?
This isn't a real regression: chk_bk_fetch_pool() already calls strlen() on the same buffer, and uuid_parse() also reads it. But the change does add another reader that relies on the byte after the key.
Could we keep the helper as it is on master and fix only the label path? daos_label_is_valid() has already computed a bounded length:
--- a/src/include/daos_prop.h
+++ b/src/include/daos_prop.h
- if (maybe_uuid && daos_is_valid_uuid_string(label, UUID_SST_NONE))
+ if (maybe_uuid && len == DAOS_UUID_STR_SIZE - 1 &&
+ daos_is_valid_uuid_string(label, UUID_SST_NONE))
This fixes -clone, leaves the checker untouched, and the new test case still covers it. I'll file a separate ticket for the checker to use key->iov_len and a local NUL-terminated copy instead of strlen() on the raw key.
There was a problem hiding this comment.
I don't think fixing only the label path is the right approach: the helper would stay broken for every other existing and future caller that passes a NUL-terminated string
(<uuid>-clone still counts as a UUID).
What I propose instead is to keep the string helper correct and add a variant for raw bytes + length:
daos_is_valid_uuid_string(const char *uuid, int sensitive): same name and signature; bounded bystrnlen(uuid, DAOS_UUID_STR_SIZE)and calls the_iovvariant.daos_is_valid_uuid_iov(const d_iov_t *iov, int sensitive): new, reads at mostiov_lenbytes. The checker callbacks pass theirkeydirectly, so nothing reads past the
akey.
For the checker ticket, I don't think a NUL-terminated copy is needed. All three sys_db callbacks receive the akey as a d_iov_t *key, so the length is always available, but
each one keeps only key->iov_buf and drops iov_len:
Callback (has key) |
Passes only key->iov_buf to |
|---|---|
| chk_pools_pause_cb | chk_bk_fetch_pool(), chk_bk_update_pool() |
| chk_pools_cleanup_cb | chk_bk_fetch_pool(), chk_bk_delete_pool() |
| chk_pools_load_from_db | chk_bk_fetch_pool(), uuid_parse() |
The callees then recover the length the length-less way:
chk_bk_fetch_pool(),chk_bk_update_pool(),chk_bk_delete_pool()callstrlen()on the key and log it with%s. They could take thed_iov_tinstead. Their other
callers (chk_pool_start_one, chk_pools_load_list, chk_pools_update_bk) already have a real string from chk_uuid_unparse() and would just wrap it.uuid_parse()needs a NUL-terminated string, and its return value is ignored. If the byte after the key isn't\0, libuuid sees a length other than 36, fails, anduuidis
used uninitialized. Parsing is no harder than the validation DAOS already does, so a matchingdaos_uuid_parse_iov()would replace it and its result could be checked.
What do you think?
Signed-off-by: Or Hayat <or.hayat@volumez.com>
Add daos_is_valid_uuid_iov() for buffers without a terminator, make daos_is_valid_uuid_string() a bounded wrapper over it, and pass the checker's raw akeys to the iov form. Signed-off-by: Or Hayat <or.hayat@volumez.com>
mchaarawi
left a comment
There was a problem hiding this comment.
i still think there is an issue with the checker overreading after the check (this is an existing behavior that requires a different PR).
|
@mchaarawi Is there already a PR to run this in our CI before landing? |
i just pushed #19326 to get it through our CI |
| d_iov_t iov; | ||
|
|
||
| iov.iov_buf = (void *)uuid; | ||
| iov.iov_buf_len = DAOS_UUID_STR_SIZE; |
There was a problem hiding this comment.
How do we guarantee uuid is not shorter than DAOS_UUID_STR_SIZE? If shorter, then the buffer is out of boundary? It is not fatal if we do not access the space after the uuid tail, but it is potential risk.
There was a problem hiding this comment.
Fixed: iov_buf_len is now set to the strnlen() result. It was a true bug but wasn't a real risk before either, since nothing reads that field, but now it's correct.
@mchaarawi this needs to be moved to mschaara/19697 (#19326) if you want to merge that one instead.
There was a problem hiding this comment.
Fixed: iov_buf_len is now set to the strnlen() result. It was a true bug but wasn't a real risk before either, since nothing reads that field, but now it's correct. @mchaarawi this needs to be moved to mschaara/19697 (#19326) if you want to merge that one instead.
we will merge this one. the other one is just for testing CI since our CI does not work with external branches yet. i updated it
Signed-off-by: Or Hayat <or.hayat@volumez.com>
|
#19326 passed all tests |
daos_is_valid_uuid_string() checked strnlen(uuid, 36) == 36, which is true for any string of at least 36 characters, so a label such as -clone was taken for a UUID and rejected by daos_label_is_valid(). Read one character past 36 so only an exact 36-character string passes.
Steps for the author:
After all prior steps are complete: