Skip to content

fix: ensuring that TO_STRING conversion functions do not panic - #1903

Open
Angus-Bethke-Bachmann wants to merge 10 commits into
masterfrom
anbt/PRG-4692
Open

fix: ensuring that TO_STRING conversion functions do not panic#1903
Angus-Bethke-Bachmann wants to merge 10 commits into
masterfrom
anbt/PRG-4692

Conversation

@Angus-Bethke-Bachmann

Copy link
Copy Markdown
Contributor

Added

  • Missing TO_STRING functions

Changed

  • Ensuring that there are no panics when invoked
  • Clean up and consolidation of functions where possible

Testing

  • Updated tests to include new cases where possible
  • Added a lit suite for the TO_STRING functions

Refs: PRG-4692

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Build Artifacts

🐧 Linux

Artifact Link Size
deb-x86_64 Download 38.4 MB
schema Download 0.0 MB
stdlib Download 32.4 MB
plc-x86_64 Download 43.5 MB
deb-aarch64 Download 30.8 MB
plc-aarch64 Download 43.4 MB

From workflow run

🪟 Windows

Artifact Link Size
stdlib.lib Download 4.0 MB
stdlib.dll Download 0.1 MB
plc.exe Download 38.3 MB

From workflow run

*
******************************************************************************)
VAR_GLOBAL CONSTANT
__BOOL_STRING_LENGTH : DINT := 5;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure this will show up as you intent. The hover on IDE will probably not resolve that constant

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, I switched to literals.

assert_eq!("TRUE", terminated_str(&dest));
unsafe { WORD_TO_STRING_EXT(u16::MAX, dest.as_mut_ptr()) };
assert_eq!("65535", terminated_str(&dest));
unsafe { LWORD_TO_STRING_EXT(u64::MAX, dest.as_mut_ptr()) };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Unsigned tests seem to be missing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added


unsafe { LWORD_TO_STRING_EXT(input, dest.as_mut_ptr()) };

assert_eq!(input.to_string(), terminated_str(&dest));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

cheater

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👀

assert_eq!("-99999999999999.250000", terminated_str(&dest));

unsafe { LREAL_TO_STRING_EXT(f64::NEG_INFINITY, dest.as_mut_ptr()) };
assert_eq!("-inf", terminated_str(&dest));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

+inf?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

unsafe { REAL_TO_STRING_EXT(1.5e7, dest.as_mut_ptr()) };
assert_eq!("1.500000e7", terminated_str(&dest));

unsafe { REAL_TO_STRING_EXT(-999_999.25, dest.as_mut_ptr()) };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

inf and nan?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

printf('%s$N', REF(DT_TO_STRING(DT#2106-02-07-06:28:15))); // CHECK-NEXT: DT#2106-02-07-06:28:15
printf('%s$N', REF(TOD_TO_STRING(TOD#1:2:3))); // CHECK-NEXT: TOD#01:02:03
printf('%s$N', REF(TOD_TO_STRING(TOD#23:59:59.999))); // CHECK-NEXT: TOD#23:59:59.999
printf('%s$N', REF(DATE_TO_STRING(-1))); // CHECK-NEXT: D#2106-02-07

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

maybe compare with codesys here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It doesn't compile at all in CODESYS. We should probably consider adding some validations for date literals to mirror the functionality.

@ghaith

ghaith commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-07T07:31:43.981311Z 8341c11 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8341c1146f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +78 to +80
write_terminated(dest, STRING_CAPACITY, format_args!("{input:.6}"));
} else {
write_terminated(dest, STRING_CAPACITY, format_args!("{input:.6e}"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bound writes to the actual destination capacity

When LREAL_TO_STRING_EXT receives a normal default STRING, as it does in the existing lreal_to_string_conversion test, the destination has 81 bytes, but this passes 2048 to write_terminated, which creates a 2047-byte mutable slice from that pointer. This is undefined behavior even when the formatted value is short, and a smaller permitted VAR_IN_OUT string can cause a direct buffer overflow. TL;DR: Do not assume that every caller provides a STRING[2048] buffer.

Useful? React with 👍 / 👎.

ghaith added a commit that referenced this pull request Sep 10, 2026
Problem: The *_TO_STRING conversions cover only a few widths, format the
short date and time types without their literal prefixes, and can panic
on unexpected input or overflow their result buffers.

Solution: Move the TO_STRING and STRING_TO declarations into dedicated
standard library files, add the missing BOOL, integer and bit-string
conversions with well defined result lengths, and format date and time
values with their literal prefixes through one panic-free writer. Lengths
are sized for the 64-bit nanosecond types used on this release line.

Backport of #1903.

Refs: PRG-4692

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

0 findings in 1s for $0.00 between 43ede05 (master) and 1cc84d1 (anbt/PRG-4692)

Angus-Bethke-Bachmann and others added 10 commits September 10, 2026 11:12
Problem: The TO_STRING writers formatted through a 2047-byte slice built over the destination pointer, so a caller that hands LREAL_TO_STRING_EXT a shorter STRING lends out memory it does not own, and the REAL, LREAL and 64-bit date and time conversions still assumed a STRING[2048] result.

Solution: Format through a writer that copies at most the declared result length behind the pointer, and give the REAL, LREAL, LDT, LDATE and LTOD conversions exact result lengths in their declarations.
Problem: LDT_TO_STRING and LDATE_TO_STRING returned bare ISO text while every other date and time conversion carries the literal prefix of its type.

Solution: Emit the LDT# and LDATE# prefixes and size the declared results for them.
…iteral

Problem: The temporal literal test still expects unprefixed TIME, LTIME, LTOD and DT output, and the duration test spells LTIME#106752d, which overflows the 64-bit nanosecond range and is rejected at compile time since the literal grammar rework.

Solution: Prefix the expected output and remove the overflowing literal; the untyped 9223372800000000000 case next to it still covers the wrapped runtime value.
@github-actions

Copy link
Copy Markdown

0 findings in 4m 32s for $0.15 between f216425 (master) and c961249 (anbt/PRG-4692)

@ghaith
ghaith self-requested a review September 10, 2026 12:13
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