{"record":{"id":"4ceb59e113cbde1c","repo":"ramensoftware/windhawk","slug":"value-contains-a-nul-character-registry","errorCode":null,"errorMessage":"value contains a NUL character","messagePattern":"value contains a NUL character","errorType":"validation","errorClass":"SettingsError","httpStatus":null,"severity":"error","filePath":"src/windhawk-core/windows/src/registry.rs","lineNumber":503,"sourceCode":"    }\n}\n\nimpl SettingsTree for RegistryTree {\n    fn get_string(&self, name: &str) -> Result<Option<String>, SettingsError> {\n        Ok(match self.query_raw(name)? {\n            Some((t, bytes)) if t == REG_SZ => Some(decode_sz(&bytes)),\n            _ => None,\n        })\n    }\n\n    fn set_string(&mut self, name: &str, value: &str) -> Result<(), SettingsError> {\n        // A REG_SZ is defined up to its first NUL, so a value carrying an\n        // embedded one reads back truncated - here (`decode_sz`), in the C++\n        // engine, and in regedit alike. Refuse it, as the INI backend does, so\n        // the same write fails the same way in both storage modes instead of\n        // losing the tail in one of them.\n        if value.contains('\\0') {\n            return Err(self.err(\"set\", 0, \"value contains a NUL character\"));\n        }\n        // REG_SZ includes the terminating NUL, matching the C++\n        // (wcslen+1)*sizeof(WCHAR) write.\n        let mut wide = to_wide(value);\n        let bytes: Vec<u8> = std::mem::take(&mut wide)\n            .into_iter()\n            .flat_map(u16::to_le_bytes)\n            .collect();\n        self.set_raw(name, REG_SZ, &bytes)\n    }\n\n    fn get_int(&self, name: &str) -> Result<Option<i32>, SettingsError> {\n        Ok(match self.query_raw(name)? {\n            Some((t, bytes)) if t == REG_DWORD => decode_dword(&bytes),\n            _ => None,\n        })\n    }\n","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/ramensoftware/windhawk/blob/61d99ed8e182e1af1b60109612b6763ad1b4b74e/src/windhawk-core/windows/src/registry.rs#L485-L521","documentation":"set_string refuses string values containing an embedded NUL, because REG_SZ is defined up to its first NUL: everything after it would be silently lost on read-back by this library, the C++ engine, and regedit alike. Refusing the write keeps INI and registry backends failing the same way instead of silently truncating.","triggerScenarios":"Calling set_string with a string containing '\\0', usually from wide-string/UTF-16 conversions, fixed-size buffers converted to String without trimming, or concatenating raw bytes into a string.","commonSituations":"Interop code turning WCHAR buffers (with padding NULs) into Strings; values assembled from C strings with terminator included; decrypting/decompressing data that yields embedded NULs.","solutions":["Trim at the first NUL before storing: value.split('\\0').next().unwrap_or(\"\").","Sanitize the input at the boundary (reject or strip NULs with an explicit validation error).","If binary data with NULs must be stored, use set_binary (hex-encoded) instead of set_string.","Fix the producer of the string to not include terminators (e.g. convert UTF-16 with correct length)."],"exampleFix":"// before\nlet s = String::from_utf16_lossy(&wbuf); // may embed \\0\nsettings.set_string(\"val\", &s)?; // Err\n// after\nlet s = String::from_utf16_lossy(&wbuf);\nlet s = s.split('\\0').next().unwrap_or(\"\").to_string();\nsettings.set_string(\"val\", &s)?;","handlingStrategy":"validation","validationCode":"fn assert_no_nul(v: &str) -> Result<(), String> {\n    if v.contains('\\0') { Err(\"value contains NUL\".into()) } else { Ok(()) }\n}","typeGuard":"fn is_nul_free(v: &str) -> bool { !v.contains('\\0') }","tryCatchPattern":"match settings.set_string(\"k\", &v) {\n    Err(e) if e.message().contains(\"NUL\") => eprintln!(\"strip NULs before storing\"),\n    r => r?,\n}","preventionTips":["Convert wide buffers with explicit lengths, not including terminators.","Route binary data (which may contain NULs) to set_binary instead of set_string.","Sanitize strings at system boundaries (interop, IPC, decryption).","Test round-trips with strings containing control characters."],"tags":["registry","validation","nul","string"],"backgroundTag":"invalid-argument-value","analyzedSha":"61d99ed8e182e1af1b60109612b6763ad1b4b74e","analyzedAt":"2026-09-12T14:02:41.115Z","contentChangedAt":"2026-09-12T14:02:41.115Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}