Skip to content

Commit 0cfbc97

Browse files
Evgeni Raikhelclaude
andcommitted
Fix WMF get_xu_range() truncating multi-byte XU control ranges to 4 bytes
ReadFromBuffer() capped every min/max/step/def copy at min(sizeof(uint32_t), length) - correct for classic scalar PU/CT controls (a value is genuinely at most a 4-byte int there), but silently truncated composite XU controls whose real wire size exceeds 4 bytes: the destination vector was correctly sized to the full option_range_size, yet only its first 4 bytes were ever populated from the device's real response, leaving every field past that offset at std::vector's zero-init default regardless of what the device actually reported. Confirmed against a real D555 running RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL (38-byte rs2_minz_control): get_composite_option_range_as() previously returned all-zero bounds for every field past ctl_id; after this fix it returns real, sane values matching the documented ranges in rs_hkr_minz_control.h (e.g. disparity_shift max=512, threshold max=65535, step=1 across the board). Verified scalar option ranges (Exposure, Gain, Laser Power, etc.) are unaffected, since length was already <=4 there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent c05c215 commit 0cfbc97

1 file changed

Lines changed: 14 additions & 6 deletions

File tree

src/mf/mf-uvc.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,16 @@ namespace librealsense
354354
if (pHeader->MembersCount < 1)
355355
throw std::exception("no data ksprop");
356356
357-
// The data fields are up to four bytes
358-
auto field_width = std::min(sizeof(uint32_t), (size_t)length);
357+
// Each entry in the underlying KS reply is exactly `length` bytes wide (pStruct is
358+
// advanced by `length` between entries below) - so the copy amount must be `length`
359+
// too, not an artificial 4-byte cap. That cap was correct for classic scalar PU/CT
360+
// controls (a value is genuinely at most a 4-byte int there, so length itself is <=4
361+
// and this was a no-op), but silently truncated multi-field composite XU controls
362+
// (e.g. HKR MinZ Control's 38-byte rs2_minz_control, RS2_COMPOSITE_OPTION_HKR_MINZ_CONTROL):
363+
// the destination vector was correctly sized to the full option_range_size, but only
364+
// its first 4 bytes ever got populated from the device's real response - every field
365+
// past that offset silently stayed at std::vector's zero-init default, regardless of
366+
// what the device actually reported.
359367
auto option_range_size = std::max(sizeof(uint32_t), (size_t)length);
360368
switch (pHeader->MembersFlags)
361369
{
@@ -370,13 +378,13 @@ namespace librealsense
370378
371379
auto pStruct = next_struct;
372380
cfg.step.resize(option_range_size);
373-
std::memcpy( cfg.step.data(), pStruct, field_width );
381+
std::memcpy( cfg.step.data(), pStruct, length );
374382
pStruct += length;
375383
cfg.min.resize(option_range_size);
376-
std::memcpy( cfg.min.data(), pStruct, field_width );
384+
std::memcpy( cfg.min.data(), pStruct, length );
377385
pStruct += length;
378386
cfg.max.resize(option_range_size);
379-
std::memcpy( cfg.max.data(), pStruct, field_width );
387+
std::memcpy( cfg.max.data(), pStruct, length );
380388
return;
381389
}
382390
case KSPROPERTY_MEMBER_VALUES:
@@ -394,7 +402,7 @@ namespace librealsense
394402
}
395403
396404
cfg.def.resize(option_range_size);
397-
std::memcpy( cfg.def.data(), next_struct, field_width );
405+
std::memcpy( cfg.def.data(), next_struct, length );
398406
}
399407
return;
400408
}

0 commit comments

Comments
 (0)