Skip to content

schema diff, types, parsers REFACTOR bound the remaining string writes - #2628

Open
aravindor wants to merge 1 commit into
CESNET:develfrom
aravindor:harden-remove-unbounded-str
Open

aravindor wants to merge 1 commit into
CESNET:develfrom
aravindor:harden-remove-unbounded-str

Conversation

@aravindor

@aravindor aravindor commented Nov 10, 2026 •

Copy link
Copy Markdown

This is a hardening change that bounds the remaining sprintf() writes in
src/, following the same approach as #2625.

What changes

  • sprintf(...) becomes snprintf(..., <bound>, ...) at every remaining
    call site, where <bound> is the real capacity of the target:
    sizeof buf for stack buffers, LY_NUMBER_MAXLEN for the number
    buffers allocated with that size.
    Files: schema_diff_tree.c, plugins_types/decimal64.c,
    plugins_types/ipv4_address_prefix.c, plugins_types/ipv6_address_prefix.c,
    tree_data_common.c.
  • tree_schema.c: lysc_path_until() writes "/" with snprintf() bounded
    by the caller's buffer length instead of strcpy().
  • parser_yang.c and parser_yin.c: the pattern modifier parsers duplicate
    the pattern with strdup() instead of malloc() + strcpy().
  • parser_yin.c: the revision-date parser copies LY_REV_SIZE bytes, the
    length lys_check_date() has just verified, instead of strcpy().

After review, the strcpy() calls in plugins_exts/schema_mount.c and
validation.c stay as they are.

Not a bug fix

The current size math at these sites is already correct: the formatted
output fits the target in every reachable case, so this does not fix any
observable overflow. It is defense in depth. The value is that the bound
is now stated in the code instead of being an invariant you have to
re-derive.

Testing

  • Debug build with -fsanitize=address,undefined, 0 warnings.
  • ctest 72/72 pass under ASan + UBSan, including the format check.

Comment thread src/plugins_exts/schema_mount.c Outdated
Comment on lines +1525 to +1526
memcpy(*mem, orig_sm_data->shared->schemas[i].mount_point,
strlen(orig_sm_data->shared->schemas[i].mount_point) + 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These really do not make sense, I think, so please keep it the way it was.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted, both are strcpy() again.

Comment thread src/plugins_exts/schema_mount.c Outdated
Comment on lines +1531 to +1532
memcpy(*mem, orig_sm_data->shared->schemas[i].content_id,
strlen(orig_sm_data->shared->schemas[i].content_id) + 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar, does not make sense changing it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted.

Comment thread src/parser_yang.c Outdated
Comment on lines +2209 to +2211
buf = malloc(strlen(restr->arg.str) + 1);
LY_CHECK_ERR_GOTO(!buf, LOGMEM(PARSER_CTX(ctx)); ret = LY_EMEM, cleanup);
strcpy(buf, restr->arg.str);
memcpy(buf, restr->arg.str, strlen(restr->arg.str) + 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strdup() should be used instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, it uses strdup() now. That needed #define _GNU_SOURCE in parser_yang.c, the same way parser_yin.c has it.

Comment thread src/validation.c Outdated
LYA_FOR(slist->uniques[u], v) {
if (v) {
strcpy(ptr, " ");
memcpy(ptr, " ", 2);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, makes no sense. Getting rid of strcpy in favor of memcpy just because is not justified, in my opinion.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted.

The schema diff tree, the decimal64 / ipv4-prefix / ipv6-prefix canonical
value printers, the date/time helpers and lysc_path_until() still built
strings with sprintf()/strcpy() into buffers sized by hand. Each buffer is
already large enough, but the writes were unbounded. Convert them to
snprintf() bounded by the buffer (sizeof or the allocation size). Output
is unchanged.

The YANG and YIN pattern modifier parsers duplicated the pattern with
malloc() + strcpy(), use strdup() there. The YIN revision-date parser
copies LY_REV_SIZE bytes, the length lys_check_date() has just verified.
@aravindor
aravindor force-pushed the harden-remove-unbounded-str branch from cc178f6 to 2b69bd2 Compare November 13, 2026 17:09
@aravindor

Copy link
Copy Markdown
Author

Thanks for the review. Updated:

  • schema_mount.c and validation.c are back to strcpy(), they are no longer part of the diff.
  • parser_yang.c uses strdup() instead of malloc() + copy.
  • The commit message no longer says that the last unbounded writes are gone.

What is left is the sprintf() to snprintf() conversion, strdup() in the two pattern modifier parsers, and the LY_REV_SIZE copy in the YIN revision-date parser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants