Skip to content

[vioscsi] Limit maximum LUN index in TransportReset() and ParamChange()#1512

Draft
benyamin-codez wants to merge 1 commit intovirtio-win:masterfrom
benyamin-codez:vioscsi-max-lun-oob-protect
Draft

[vioscsi] Limit maximum LUN index in TransportReset() and ParamChange()#1512
benyamin-codez wants to merge 1 commit intovirtio-win:masterfrom
benyamin-codez:vioscsi-max-lun-oob-protect

Conversation

@benyamin-codez
Copy link
Copy Markdown
Contributor

The Lun variable in TransportReset() and ParamChange() is rightly declared as a UCHAR value. However, the value is derived from two UCHAR members of a VirtIOSCSIEvent struct (lun[2] and lun[3]). When the lun[2] member is left shifted 8 bits it is promoted to type USHORT, and then bitwise OR'ed with lun[3]. This leads to an integer truncation if the LUN index is greater than 256. Whilst Storport does not support more than 255 LUNs per Target as defined by SCSI_MAXIMUM_LUNS_PER_TARGET, a malicious or misconfigured hypervisor could send an event for a higher LUN index. In TransportReset() this could result in another LUN being reset, and in ParamChange() this could result in processing parameter changes for the wrong LUN.

The solution in this commit is to first obtain the LUN index via new USHORT variable lun_candidate and then check if it is within the bounds of SCSI_MAXIMUM_LUNS_PER_TARGET. If out-of-bounds, we log a warning message - including the reason for the event - and return without further processing. Otherwise we cast lun_candidate to Lun as a UCHAR and continue.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @benyamin-codez, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness of the vioscsi driver by addressing a potential issue where LUN indices exceeding the UCHAR limit could lead to incorrect processing in TransportReset() and ParamChange() functions. By correctly calculating and validating LUN indices, the driver is now protected against malformed input from hypervisors, ensuring that operations are only performed on valid and supported LUNs and preventing unintended side effects.

Highlights

  • LUN Index Calculation Correction: The calculation of the LUN index in TransportReset() and ParamChange() functions has been updated to use a USHORT variable (lun_candidate) to prevent integer truncation when combining evt->lun[2] and evt->lun[3], which could occur if the LUN index exceeded 255.
  • LUN Index Validation: Introduced a validation step to check if the calculated lun_candidate is within the supported range defined by SCSI_MAXIMUM_LUNS_PER_TARGET. This prevents processing of out-of-bounds LUN indices.
  • Error Handling for Out-of-Bounds LUNs: If an out-of-bounds LUN index is detected, a warning message is now logged using RhelDbgPrint, including the problematic LUN index, the maximum supported LUNs, and the event reason. The function then returns early without further processing, preventing potential incorrect operations.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@YanVugenfirer
Copy link
Copy Markdown
Collaborator

[Jenkins CI]: Can one of the admins verify this patch?

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly addresses a potential integer truncation vulnerability by adding bounds checking for the LUN index in TransportReset() and ParamChange(). This is a good security enhancement. I've provided a couple of suggestions to improve code correctness and readability, and another to address code duplication for better maintainability.

Comment on lines +1820 to +1831
if (lun_candidate > SCSI_MAXIMUM_LUNS_PER_TARGET - 1)
{
RhelDbgPrint(TRACE_LEVEL_WARNING,
" The LUN specified (%d) is out-of-bounds."
" Storport can only address a maximum of %d LUNs."
" The event reason was 0x%x."
" Returning...\n",
lun_candidate,
SCSI_MAXIMUM_LUNS_PER_TARGET,
evt->reason);
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This bounds check is a good security improvement. For correctness and readability, I suggest two minor changes:

  1. Use the %u format specifier for lun_candidate in RhelDbgPrint since it's an unsigned type (USHORT).
  2. Change the comparison to lun_candidate >= SCSI_MAXIMUM_LUNS_PER_TARGET, which is equivalent and arguably more readable than > SCSI_MAXIMUM_LUNS_PER_TARGET - 1.

Additionally, this validation logic is duplicated from TransportReset. To improve maintainability, consider extracting this logic into a static helper function.

    if (lun_candidate >= SCSI_MAXIMUM_LUNS_PER_TARGET)
    {
        RhelDbgPrint(TRACE_LEVEL_WARNING,
                     " The LUN specified (%u) is out-of-bounds."
                     " Storport can only address a maximum of %d LUNs."
                     " The event reason was 0x%x."
                     " Returning...\n",
                     lun_candidate,
                     SCSI_MAXIMUM_LUNS_PER_TARGET,
                     evt->reason);
        return;
    }

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.

Will do 1 & 2...
Re static helper - this would only be for two callers. Happy to be guided on this by maintainers...

The 'Lun' variable in TransportReset() and ParamChange() is rightly declared
as a UCHAR value. However, the value is derived from two UCHAR members of a
'VirtIOSCSIEvent' struct ('lun[2]' and 'lun[3]'). When the 'lun[2]' member is
left shifted 8 bits it is promoted to type USHORT, and then bitwise OR'ed with
'lun[3]'. This leads to an integer truncation if the LUN index is greater than
256. Whilst Storport does not support more than 255 LUNs per Target as defined
by SCSI_MAXIMUM_LUNS_PER_TARGET, a malicious or misconfigured hypervisor could
send an event for a higher LUN index. In TransportReset() this could result in
another LUN being reset, and in ParamChange() this could result in processing
parameter changes for the wrong LUN.

The solution in this commit is to first obtain the LUN index via new USHORT
variable 'lun_candidate' and then check if it is within the bounds of
SCSI_MAXIMUM_LUNS_PER_TARGET. If out-of-bounds, we log a warning message -
including the reason for the event - and return without further processing.
Otherwise we cast 'lun_candidate` to 'Lun' as a UCHAR and continue.

Signed-off-by: benyamin-codez <115509179+benyamin-codez@users.noreply.github.com>
@benyamin-codez benyamin-codez force-pushed the vioscsi-max-lun-oob-protect branch from 72a4c50 to 0411ea3 Compare February 1, 2026 13:53
@benyamin-codez
Copy link
Copy Markdown
Contributor Author

@YanVugenfirer

I've only tested this for normal operation. To trigger the fault path this will likely require testing with a maliciously crafted version of say virtio_scsi_hotunplug() from qemu/hw/scsi/virtio-scsi.c to change the Lun used and blindly push the event. Did we need to go to that extent? Maybe the QEMU storage team has some toolkit to make light work of this?

cc: @kevmw

@YanVugenfirer
Copy link
Copy Markdown
Collaborator

@benyamin-codez I think maybe I am missing something - Lun is calculated, but never used.
Also it looks like some big endian to little endian conversion (I am not a SCSI expert, I don't know how LUN should be represented in the request).

So I think this PR is redundant.

@benyamin-codez
Copy link
Copy Markdown
Contributor Author

I think maybe I am missing something - Lun is calculated, but never used. Also it looks like some big endian to little endian conversion (I am not a SCSI expert, I don't know how LUN should be represented in the request).

So I think this PR is redundant.

@YanVugenfirer

Wow..! That got past me... I don't think you're missing anything - I just followed AI up the garden path... 8^d

Not just Lun isn't used, but TargetId also. Maybe they had some utility during a debugging session at some stage...

On the SCSI side, iinm, we use a vendor-specific addressing method derived from SAM-4, per the VirtIO spec:

lun addresses the REPORT LUNS well-known logical unit, or a target and logical unit in the virtio-scsi device’s SCSI domain.

When used to address the REPORT LUNS logical unit, lun is 0xC1, 0x01 and six zero bytes. The virtio-scsi device SHOULD implement the REPORT LUNS well-known logical unit.

When used to address a target and logical unit, the only supported format for lun is: first byte set to 1, second byte set to target, third and fourth byte representing a single level LUN structure, followed by four zero bytes. With this representation, a virtio-scsi device can serve up to 256 targets and 16384 LUNs per target. The device MAY also support having a well-known logical units in the third and fourth byte.

Most addressing methods that make use of the additional bytes typically left shift each byte to derive a concatenated value, so that behaviour is not surprising. Having said all that, Storport is still limited to 255 LUNs (0 to 254)...

I do see some value to adding some tracing here to make use of these variables. It could help identify various problems in these pathways. For example, ejecting the hard disk currently doesn't work. At a guess, that process seems like it might at least touch one of these functions - but maybe not. A log entry for ParamChange() would certainly be insightful and probably of some benefit, even if it just acknowledges whether or not a notification was sent. In any case, if you agree adding some more tracing here is of benefit, then probably best to let this one proceed and do that in a new PR.

However, if we don't use Lun and TargetId (or Target as proposed in PR #1511) in a trace, we should probably just remove them, yes? I could amend PR #1511 to remove them instead, or just raise a new PR to do that.

Please do let me know how you would like to proceed.

Best regards,
Ben

@YanVugenfirer
Copy link
Copy Markdown
Collaborator

Hi @benyamin-codez.

Let's remove Lun.
Regarding TragetID, let me check PR #1511 first. I will do it later.

Thanks!

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