diff --git a/Makefile b/Makefile index e687b2c..ceeb1ce 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ UPSTREAM_INCLUDES := \ # compiles; -MMD tracks them properly from then on. $(APP_OBJS) $(UPSTREAM_OBJS) $(SOLIDSYSLOG_PLATFORM_OBJS): | $(FATFS_STAGED_HEADERS) -$(APP_OBJS) $(SOLIDSYSLOG_PLATFORM_OBJS): CFLAGS := $(COMMON_CFLAGS) $(APP_WARNINGS) $(APP_INCLUDES) $(MBEDTLS_USER_CONFIG) +$(APP_OBJS) $(SOLIDSYSLOG_PLATFORM_OBJS): CFLAGS := $(COMMON_CFLAGS) $(APP_WARNINGS) $(APP_INCLUDES) $(MBEDTLS_USER_CONFIG) $(SOLIDSYSLOG_USER_TUNABLES) $(UPSTREAM_OBJS): CFLAGS := $(COMMON_CFLAGS) $(UPSTREAM_WARNINGS) $(UPSTREAM_INCLUDES) # Our objects, the upstream ones and the platform packs link in loose; mbedTLS diff --git a/README.md b/README.md index 514d071..e35160c 100644 --- a/README.md +++ b/README.md @@ -10,46 +10,46 @@ It builds on a baseline that simulates the sort of device you might be adding th measures itself: see [docs/baseline.md](docs/baseline.md) for what the baseline is, how the figures are made, and how to run it. -## This stage — AES-GCM at rest +## This stage — Right-sized -Replace the HMAC policy with authenticated encryption. Tamper-evidence establishes that a stored -record was not altered; it does nothing to stop anyone reading it. AES-256-GCM encrypts the body, -authenticates the record header as associated data, and puts the nonce and tag in the trailer. +Fit the compile-time sizes to what this device uses, now that every collaborator is in place. + +The message cap comes first, because the ring, the store's record buffer and the formatter frame on +both task stacks all follow it. ```c -struct SolidSyslogMbedTlsAesGcmPolicyConfig gcmConfig = {.GetKey = SyslogStoreKey, .Rng = rng}; +/* app/config/solid_syslog_tunables.h */ +#define SOLIDSYSLOG_MAX_MESSAGE_SIZE 400U -.SecurityPolicy = SolidSyslogMbedTlsAesGcmPolicy_Create(&gcmConfig), +#define SOLIDSYSLOG_ADDRESS_POOL_SIZE 1U +#define SOLIDSYSLOG_TCP_STREAM_POOL_SIZE 1U +#define SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE 1U ``` -GCM needs a fresh nonce per record and mbedTLS has no context-free RNG, so the policy takes the -device's DRBG as well as the key. That is the only wiring difference from the HMAC policy. +The worst case measured here is 345 octets: the four SD-ELEMENTs with both counters at full 32-bit +width and both addresses at fifteen characters, plus a short message. 400 allows for longer messages +on this device. Anything longer is truncated rather than dropped. -The store key does not change. Its name states what it protects rather than which algorithm protects -it, so escalating the policy needs no new key provisioned. +The pool defaults suit a device running several transports at once. This one runs a single sender +over a single stream to a single destination. -These are separate decisions and the second does not follow from the first. A device that only needs -to prove records were not altered can stop at the HMAC. +The overrides reach the library through `SOLIDSYSLOG_USER_TUNABLES_FILE`, an absolute path quoted +for the preprocessor and given to every group that includes a SolidSyslog header: Core, the platform +sources and this application. They change struct sizes, and a build where only some translation +units saw them would disagree about how big those structs are. -The pipeline element now derives both of its values from what the device holds, and each falls back -to the weakest honest answer when the credential behind it is missing: +The ring drops from eight records to four. The store holds a backlog, so the ring only has to absorb +what can be logged while the service task is sending. -```c -s_sd[3] = SyslogPipelineSd_Init( - ((clientChain != NULL) && (clientKey != NULL)) ? "mtls" : "tls", (rng != NULL) ? "aes-256-gcm" : "none" -); -``` - -```text -... [logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] device started -``` +The task stacks go last, at twice their measured high-water marks rounded up to a whole +`configMINIMAL_STACK_SIZE`. -**When you need it.** If a disk that leaves the device would give something away — records naming -users, addresses, process values, or anything else you would not publish. +**When you need it.** Once the pipeline is complete. Sizing earlier means sizing against a device +that is still missing collaborators. -**Cost above baseline: Flash +13,788 B, RAM +39,536 B.** +**Cost above baseline: Flash +13,788 B, RAM +36,048 B.** @@ -80,6 +80,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage. | Private SD-ELEMENT | a record that states the protection its own log pipeline was under | +13,556 | +37,472 | | Mutual TLS | a collector that knows which device sent the record, not just that one did | +13,624 | +39,524 | | AES-GCM at rest | spooled records unreadable to anyone holding the disk, not just unforgeable | +13,788 | +39,536 | +| Right-sized | the pools, the ring and the task stacks fitted to what this device uses | +13,788 | +36,048 | *Deltas are bytes above the baseline, which is itself Flash 350,124 B, RAM 111,192 B.* diff --git a/app/AppConfig.h b/app/AppConfig.h index 967bc9e..e996347 100644 --- a/app/AppConfig.h +++ b/app/AppConfig.h @@ -10,12 +10,11 @@ /* CMSDK UART0 on the mps2-an385, surfaced by QEMU over -serial stdio. */ #define DEVICE_UART0_BASE ((uintptr_t) 0x40004000U) -/* Both seams handle a whole record — the log seam formats one, the service seam - * drains one — so each holds SOLIDSYSLOG_MAX_MESSAGE_SIZE and its onward call - * beneath, and neither fits the FreeRTOS floor. Sized generously here and - * tightened against measured high-water marks once the pipeline is complete. */ -#define LOG_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 4U) -#define SERVICE_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 16U) +/* Twice the measured high-water mark, rounded up to a whole configMINIMAL_STACK_SIZE, + * or the FreeRTOS floor where that is below it. The reported figure is high-water + * usage, which does not depend on the allocation. */ +#define LOG_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 3U) +#define SERVICE_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 15U) #define LOG_TASK_PRIORITY (tskIDLE_PRIORITY + 1U) #define SERVICE_TASK_PRIORITY (tskIDLE_PRIORITY + 1U) diff --git a/app/config/solid_syslog_tunables.h b/app/config/solid_syslog_tunables.h new file mode 100644 index 0000000..f8772f0 --- /dev/null +++ b/app/config/solid_syslog_tunables.h @@ -0,0 +1,18 @@ +/* SolidSyslog compile-time overrides for this device. Reached via + * SOLIDSYSLOG_USER_TUNABLES_FILE; anything not set here keeps the library + * default from SolidSyslogTunablesDefaults.h. */ +#ifndef SOLID_SYSLOG_TUNABLES_H +#define SOLID_SYSLOG_TUNABLES_H + +/* The worst case measured here is 345 octets: the structured data at full width + * with a short message. 400 allows for longer messages on this device. Anything + * longer is truncated rather than dropped. */ +#define SOLIDSYSLOG_MAX_MESSAGE_SIZE 400U + +/* One sender, over one stream, to one destination. The defaults suit a device + * running several transports at once. */ +#define SOLIDSYSLOG_ADDRESS_POOL_SIZE 1U +#define SOLIDSYSLOG_TCP_STREAM_POOL_SIZE 1U +#define SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE 1U + +#endif /* SOLID_SYSLOG_TUNABLES_H */ diff --git a/app/syslog/Syslog.c b/app/syslog/Syslog.c index d525619..4825251 100644 --- a/app/syslog/Syslog.c +++ b/app/syslog/Syslog.c @@ -54,9 +54,9 @@ #define SYSLOG_COLLECTOR_HOST "10.0.2.2" #define SYSLOG_COLLECTOR_PORT ((uint16_t) 6515U) -/* Depth enough to absorb a burst while the sender is busy, without sizing for a - * backlog the store is there to hold. */ -#define SYSLOG_BUFFER_RECORDS 8U +/* Enough to absorb a burst logged while the service task is sending. The store + * holds a backlog, so the ring does not have to. */ +#define SYSLOG_BUFFER_RECORDS 4U /* One "NN.log" per block, on the volume the device already mounts. */ #define SYSLOG_STORE_PREFIX "syslog" diff --git a/make/solidsyslog.mk b/make/solidsyslog.mk index 13d73c7..2c0fcbe 100644 --- a/make/solidsyslog.mk +++ b/make/solidsyslog.mk @@ -11,7 +11,14 @@ SOLIDSYSLOG_LIB := $(BUILD)/libSolidSyslog.a SOLIDSYSLOG_CORE_OBJS := $(SOLIDSYSLOG_CORE_SRCS:%.c=$(OBJ_DIR)/%.o) SOLIDSYSLOG_PLATFORM_OBJS := $(SOLIDSYSLOG_PLATFORM_SRCS:%.c=$(OBJ_DIR)/%.o) -$(SOLIDSYSLOG_CORE_OBJS): CFLAGS := $(COMMON_CFLAGS) $(SOLIDSYSLOG_CORE_INCLUDES) +# Consumed via #include, so the value has to reach the compiler as a quoted C +# string literal — escaped rather than single-quoted, and absolute, because Core +# compiles without our include path on it. +SOLIDSYSLOG_USER_TUNABLES := -DSOLIDSYSLOG_USER_TUNABLES_FILE=\"$(CURDIR)/$(APP_DIR)/config/solid_syslog_tunables.h\" + +# Every object that includes a SolidSyslog header needs it: the tunables change +# struct sizes, so Core, the platforms and our own code must agree on them. +$(SOLIDSYSLOG_CORE_OBJS): CFLAGS := $(COMMON_CFLAGS) $(SOLIDSYSLOG_CORE_INCLUDES) $(SOLIDSYSLOG_USER_TUNABLES) $(SOLIDSYSLOG_LIB): $(SOLIDSYSLOG_CORE_OBJS) @mkdir -p $(@D) diff --git a/measurements/right-size.csv b/measurements/right-size.csv new file mode 100644 index 0000000..a6b3a1a --- /dev/null +++ b/measurements/right-size.csv @@ -0,0 +1,13 @@ +# right-size figures (bytes) — captured by scripts/run.sh (CAPTURE=1). +# The device reads measurements/Baseline.csv as its frozen baseline and reports current-minus-Baseline. +flash_text,363256 +flash_data,656 +static_bss,146584 +heap_used,4440 +mbedtls_peak,37244 +mbedtls_free,19076 +lwip_mem_free,7576 +lwip_pbufs_free,13 +stack_log,720 +stack_service,3740 +stack_harness,2848 diff --git a/measurements/stages.tsv b/measurements/stages.tsv index 4b8e4ea..aeeff95 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -27,3 +27,4 @@ hmac HMAC at rest stored records that cannot be edited undetected, not merely ch pipeline-sd Private SD-ELEMENT a record that states the protection its own log pipeline was under mtls Mutual TLS a collector that knows which device sent the record, not just that one did aes-gcm AES-GCM at rest spooled records unreadable to anyone holding the disk, not just unforgeable +right-size Right-sized the pools, the ring and the task stacks fitted to what this device uses diff --git a/run-report.md b/run-report.md index 8df7ba1..45b2043 100644 --- a/run-report.md +++ b/run-report.md @@ -1,4 +1,4 @@ -# solid-syslog-example — run (aes-gcm) +# solid-syslog-example — run (right-size) ## Device (self-measured) @@ -12,14 +12,14 @@ [report] key,current,baseline,used_above_baseline [report] flash_text,363256,349808,13448 [report] flash_data,656,316,340 -[report] static_bss,150072,110876,39196 +[report] static_bss,146584,110876,35708 [report] heap_used,4440,4440,0 -[report] mbedtls_peak,37136,21328,15808 -[report] mbedtls_free,19184,11440,7744 +[report] mbedtls_peak,37260,21328,15932 +[report] mbedtls_free,19060,11440,7620 [report] lwip_mem_free,7576,7576,0 -[report] lwip_pbufs_free,13,13,0 -[report] stack_log,800,120,680 -[report] stack_service,3820,52,3768 +[report] lwip_pbufs_free,14,13,1 +[report] stack_log,720,120,600 +[report] stack_service,3740,52,3688 [report] stack_harness,2848,2840,8 [report] --- end --- [device] ready @@ -29,7 +29,7 @@ ```text text data bss dec hex filename - 363248 664 150072 513984 7d7c0 /w/build/baseline.elf + 363248 664 146584 510496 7ca20 /w/build/baseline.elf ``` ## Listeners (proved before the device ran) @@ -47,23 +47,23 @@ ## Collector (syslog-ng) received ```text -wire <134>1 2026-08-16T19:53:56.430000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] device started -parsed PRIORITY=134 TIMESTAMP=2026-08-16T19:53:56+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] MSG=device started +wire <134>1 2026-08-16T19:57:02.430000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] device started +parsed PRIORITY=134 TIMESTAMP=2026-08-16T19:57:02+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] MSG=device started ``` -## Self-check (vs measurements/aes-gcm.csv) +## Self-check (vs measurements/right-size.csv) ```text OK flash_text: 363256 (expected 363256, Δ0) OK flash_data: 656 (expected 656, Δ0) - OK static_bss: 150072 (expected 150072, Δ0) + OK static_bss: 146584 (expected 146584, Δ0) OK heap_used: 4440 (expected 4440, Δ0) - OK mbedtls_peak: 37136 (expected 37248, Δ112) - OK mbedtls_free: 19184 (expected 19072, Δ112) + OK mbedtls_peak: 37260 (expected 37244, Δ16) + OK mbedtls_free: 19060 (expected 19076, Δ16) OK lwip_mem_free: 7576 (expected 7576, Δ0) - OK lwip_pbufs_free: 13 (expected 13, Δ0) - OK stack_log: 800 (expected 800, Δ0) - OK stack_service: 3820 (expected 3820, Δ0) + OK lwip_pbufs_free: 14 (expected 13, Δ1) + OK stack_log: 720 (expected 720, Δ0) + OK stack_service: 3740 (expected 3740, Δ0) OK stack_harness: 2848 (expected 2848, Δ0) ```