Repository navigation
Conversation
Two unchecked return values. On MSVC 18 both stop the build; on the pinned CI toolset neither surfaces. Neither defect is cosmetic. AvSetMmThreadCharacteristicsW returns NULL on failure, and that NULL went straight into AvRevertMmThreadCharacteristics, which does not accept it. Reported as C28183 and promoted to C2220 by TreatWarningAsError. The revert now happens only when the handle is valid; when the characteristics were never set there is nothing to revert, so that branch is a log and no call. CoCreateGuid had its HRESULT discarded (C6031 -> C2220). MonitorInfo is zero-initialised, so a failure left MonitorContainerId as GUID_NULL, which was then handed to IddCxMonitorCreate as if it were valid. The result is now checked, and on failure the driver logs and skips creating that monitor rather than registering it under GUID_NULL. Note how this presents: the first C2220 aborts the compile before the next warning is emitted, so silencing C28183 does not give a green build, it only moves the error to the next line. Both had to be fixed to find out whether anything was behind them. Verified: Debug and Release on x64 and ARM64, 0 errors, all four producing MttVDD.dll, MttVDD.inf and a signed mttvdd.cat.
Two things the rest of the driver builds on: the shared state is actually guarded, and there is a way to parse a user-supplied number that does not throw out of a UMDF callback. No behaviour change. g_Running becomes std::atomic<bool>: the pipe server thread flips it from a command while the main path reads it. g_pipeHandle is only written through SetPipeHandle()/ClearPipeHandle(), under a mutex that is deliberately not g_Mutex. vddlog() is called from inside the pipe server while that thread holds g_Mutex, so SendToPipe() cannot take g_Mutex without deadlocking on it. ClearPipeHandle() returns the previous value so the caller can close it, and the handle is withdrawn before the close. s_KnownMonitorModesMutex protects a std::vector that two IddCx callbacks can rewrite at the same time -- one clears and refills it while the other indexes it. That is a live data race, not a stale read. Readers take a snapshot under the lock instead of holding it across the IddCx calls that follow. g_MetadataMutex guards the HDR and gamma stores, which the settings loader and the IddCx callbacks both write. vdd_settings.xml, option.txt and monitor_profile.xml are user-editable, and loadSettings() runs inside the DeviceAdd callback. std::stoi and std::stof throw on a malformed value, and an exception leaving a UMDF callback crashes the host. ParseIntSafe, ParseFloatSafe and ParseDoubleSafe log the offending value and return the caller's fallback instead, and they reject trailing garbage. ParseDoubleSafe exists because the EDID chromaticity fields are double: redX 0.640 read as float and stored back as double is 0.63999998569488525390625, which then differs from the 0.640 the same file computes elsewhere. ClampToUINT16 exists because a light level above 65535 wrapped silently on the cast. Verified: Debug and Release on x64 and ARM64, 0 errors, all four producing MttVDD.dll, MttVDD.inf and a signed mttvdd.cat.
vdd_settings.xml was reparsed on every single read. It is parsed once into a cache now, and the cache is invalidated when the runtime rewriters change the file on disk. Four registry readers passed lpType = NULL to RegQueryValueExW and then handed the raw wchar_t[MAX_PATH] to std::wstring or std::stoi. Two of the four already captured the type; the other two did not. All four now require REG_SZ or REG_EXPAND_SZ, reject a length outside the buffer, drop a trailing non-terminator, and construct the string by the length the registry reported instead of scanning for a NUL that a malformed value is not guaranteed to have. GetDoubleSetting handles REG_DWORD with memcpy, because REG_DWORD writes four raw bytes with no terminator at all. A replay harness over six payloads -- well-formed REG_SZ, terminator-less REG_SZ, REG_BINARY filling the buffer, a small REG_BINARY, REG_DWORD and an empty value -- leaves the old path out of bounds in 5 of 5 adversarial cases, reaching index 519 of a wchar_t[260]. The new path leaves it in 0. The empty value is a genuine escape rather than a theoretical one: the registry writes nothing there, so the buffer keeps whatever the stack held. EnabledQuery now reports whether the key was present at all, separately from its value. The old contract collapsed "absent" and "present and false" into the same false, and DriverEntry needs the difference for the settings whose compiled default is true -- HardwareCursor ships as <HardwareCursor>true</ HardwareCursor>, and answering false for "no settings file" turned the hardware cursor off on exactly the configuration nobody had configured. EnabledOr routes the missing-key case to a caller-chosen default and keeps EnabledQuery's old contract for the 25 call sites that do not care why the answer is false. Five settings collided on the bare name "enabled" and are now qualified by path (hdr10_static_metadata/enabled and friends). Verified: Debug and Release on x64 and ARM64, 0 errors, all four producing MttVDD.dll, MttVDD.inf and a signed mttvdd.cat.
GetIntegerSetting answers -1 for a key that is in neither the registry nor the settings XML, and GetDoubleSetting answers 0.0. Neither answer is inert in the place it is used. -1 is rejected by the upper bound of the mode filter, so it works as a sentinel by accident, and only for that one consumer. Everything else that reads an integer setting has to know that -1 means "not set" and not a number. 0.0 is worse: it is inside [0,1], so a colour primary read from a key that does not exist becomes a point sitting on the corner of the triangle and passes validation without a word. The monitor then comes up with the wrong colour and nothing is logged. positiveOr() now applies the caller's fallback for both, and the settings that ship a compiled default of true are read with EnabledOr() so that "no settings file" is answered with the default rather than with false. The compiled defaults are reconciled with what vdd_settings.xml actually ships: gammaCorrection 2.4 -> 2.2 force_bit_depth "auto" -> 8 hardwareCursor false -> true maxResolutionWidth 7680 -> 10240 colour primaries BT.2020 -> Rec.709 The XML always wins at runtime, so a divergence between the two only showed up on the path with no settings file -- which is exactly the path nobody had configured. option.txt ships 10240x4320 and 8640 modes, and a ceiling of 7680 cut them off; vdd_settings.xml annotates the primaries as "sRGB color space (safe defaults)", and the BT.2020 numbers were the ones the file did not use. The settings that nothing ever read are removed rather than kept: fp16_surface_support, wide_color_gamut, hdr_tone_mapping, sdr_white_level and all eight Monitor Emulation entries were parsed and then dropped on the floor. A value in vdd_settings.xml for any of them did nothing, silently. Five settings collided on the bare name "enabled" and are now qualified by path (hdr10_static_metadata/enabled and friends), so a key that means one thing in one section cannot be read as another. Verified: Debug and Release on x64 and ARM64, 0 errors, all four producing MttVDD.dll, MttVDD.inf and a signed mttvdd.cat.
|
Closing this without merging. The work is real and it is verified -- every branch builds clean in Debug and The code stays available on the fork, split into reviewable pieces: fix/code-analysis-errors 1 file +14/-2 If any of it is ever wanted, the branches are there and this PR can be |
Summary
What the driver does when a setting is not set. One file, +103 / −69.
The sentinel is not a value
GetIntegerSettinganswers-1for a key that is in neither the registry northe settings XML, and
GetDoubleSettinganswers0.0. Neither answer is inertin the place it is used.
-1is rejected by the upper bound of the mode filter, so it works as asentinel by accident, and only for that one consumer. Everything else that
reads an integer setting has to know that
-1means "not set" and not a number.0.0is worse: it is inside[0,1], so a colour primary read from a keythat does not exist becomes a point sitting on the corner of the triangle and
passes validation without a word. The monitor then comes up with the wrong
colour and nothing is logged.
positiveOr()now applies the caller's fallback for both, and the settingsthat ship a compiled default of
trueare read withEnabledOr()so that "nosettings file" is answered with the default rather than with
false.The compiled defaults match what the XML ships
gammaCorrectionforce_bit_depthhardwareCursormaxResolutionWidthThe XML always wins at runtime, so a divergence between the two only showed up
on the path with no settings file — which is exactly the path nobody had
configured.
option.txtships 10240x4320 and 8640 modes, and a ceiling of 7680cut them off;
vdd_settings.xmlannotates the primaries as "sRGB color space(safe defaults)", and the BT.2020 numbers were the ones the file did not use.
The settings nothing read are removed
fp16_surface_support,wide_color_gamut,hdr_tone_mapping,sdr_white_leveland all eight Monitor Emulation entries were parsed and thendropped on the floor. A value in
vdd_settings.xmlfor any of them didnothing, silently.
Five settings collided on the bare name
enabledand are now qualified by path(
hdr10_static_metadata/enabledand friends), so a key that means one thing inone section cannot be read as another.
Verification
Debug and Release on x64 and ARM64 — four configurations, each producing
MttVDD.dll,MttVDD.infand a signedmttvdd.cat, withTreatWarningAsErroron.Not verified on hardware. The behaviour that changes here is what the
driver does with a setting that is not present, which is the no-settings-file
path. Installing without
vdd_settings.xmland checking that the hardwarecursor is on and the modes are not truncated would confirm it.