Skip to content

fix(toolbox): prevent invalid tile indexing in toolbox overlap calculation#17131

Open
Harxhit wants to merge 1 commit intojitsi:masterfrom
Harxhit:fix-toolbox
Open

fix(toolbox): prevent invalid tile indexing in toolbox overlap calculation#17131
Harxhit wants to merge 1 commit intojitsi:masterfrom
Harxhit:fix-toolbox

Conversation

@Harxhit
Copy link

@Harxhit Harxhit commented Mar 12, 2026

This patch improves the toolbox overlap detection logic in
react/features/toolbox/subscriber.web.ts.

Changes:

  1. Prevent invalid tile indexing
    The loop used Math.max(noOfTilesToCheck, tiles.length), which could cause
    negative indexing when noOfTilesToCheck exceeded the number of tiles.
    The iteration limit is now bounded using Math.min(...) to ensure only
    valid tile indexes are accessed.

  2. Add document guard
    A guard for the document object was added to prevent potential runtime
    issues in environments where document may be undefined (e.g., testing).

  3. Improve TypeScript readability
    Added HTMLSpanElement typing for querySelectorAll to improve type
    inference and readability.

@jitsi-jenkins
Copy link

Hi, thanks for your contribution!
If you haven't already done so, could you please make sure you sign our CLA (https://jitsi.org/icla for individuals and https://jitsi.org/ccla for corporations)? We would unfortunately be unable to merge your patch unless we have that piece :(.

@damencho
Copy link
Member

  1. Prevent invalid tile indexing
    The loop used Math.max(noOfTilesToCheck, tiles.length), which could cause
    negative indexing when noOfTilesToCheck exceeded the number of tiles.
    The iteration limit is now bounded using Math.min(...) to ensure only
    valid tile indexes are accessed.

What does this fix? How to reproduce?

@Harxhit
Copy link
Author

Harxhit commented Mar 12, 2026

  1. Prevent invalid tile indexing
    The loop used Math.max(noOfTilesToCheck, tiles.length), which could cause
    negative indexing when noOfTilesToCheck exceeded the number of tiles.
    The iteration limit is now bounded using Math.min(...) to ensure only
    valid tile indexes are accessed.

What does this fix? How to reproduce?

The issue happens when noOfTilesToCheck is larger than the number of rendered tiles.
For example:
tiles.length = 3
noOfTilesToCheck = 7
The previous loop used:

for (let i = 1; i < Math.max(noOfTilesToCheck, tiles.length); i++)

Math.max(7, 3) becomes 7, so the loop runs 6 iterations even though only 3 tiles exist.
During later iterations:

const tile = tiles[tiles.length - i];

becomes:

tiles[3 - 4] → undefined
tiles[3 - 5] → undefined

which results in accessing .querySelector() on an invalid tile reference.
The fix changes the upper bound to:

const limit = Math.min(noOfTilesToCheck, tiles.length);

ensuring the loop only iterates over existing tiles.

1 similar comment
@Harxhit
Copy link
Author

Harxhit commented Mar 12, 2026

  1. Prevent invalid tile indexing
    The loop used Math.max(noOfTilesToCheck, tiles.length), which could cause
    negative indexing when noOfTilesToCheck exceeded the number of tiles.
    The iteration limit is now bounded using Math.min(...) to ensure only
    valid tile indexes are accessed.

What does this fix? How to reproduce?

The issue happens when noOfTilesToCheck is larger than the number of rendered tiles.
For example:
tiles.length = 3
noOfTilesToCheck = 7
The previous loop used:

for (let i = 1; i < Math.max(noOfTilesToCheck, tiles.length); i++)

Math.max(7, 3) becomes 7, so the loop runs 6 iterations even though only 3 tiles exist.
During later iterations:

const tile = tiles[tiles.length - i];

becomes:

tiles[3 - 4] → undefined
tiles[3 - 5] → undefined

which results in accessing .querySelector() on an invalid tile reference.
The fix changes the upper bound to:

const limit = Math.min(noOfTilesToCheck, tiles.length);

ensuring the loop only iterates over existing tiles.

@damencho
Copy link
Member

But how did you test it with the UI, what settings did you change to be able to reproduce the issue? How do you test it was fixed?

@Harxhit
Copy link
Author

Harxhit commented Mar 13, 2026

But how did you test it with the UI, what settings did you change to be able to reproduce the issue? How do you test it was fixed?

How to reproduce the bug

  1. Natural case (real meeting – 9 participants, 2 rows)
Number of rows: 2
tilesLength: 9
noOfTilesToCheck: 4

i: 1 → tileIndex: 8
i: 2 → tileIndex: 7
i: 3 → tileIndex: 6
i: 4 → tileIndex: 5
i: 5 → tileIndex: 4
i: 6 → tileIndex: 3
i: 7 → tileIndex: 2
i: 8 → tileIndex: 1

Loop ran 8 times instead of the intended 4. We were checking tiles we never needed to.

  1. Forced reproduction (to see negative indices)
    Temporarily set:
const noOfTilesToCheck = tiles.length + 5;

Logs:

tilesLength: 3
noOfTilesToCheck: 8

i: 1 → tileIndex: 2
i: 2 → tileIndex: 1
i: 3 → tileIndex: 0
i: 4 → tileIndex: -1     ← negative index!
i: 5 → tileIndex: -2
i: 6 → tileIndex: -3
i: 7 → tileIndex: -4

Fix applied

const noOfTilesToCheck = rows === 1 ? tiles.length : DEFAULT_MAX_COLUMNS - 1;
const limit = Math.min(noOfTilesToCheck, tiles.length);

for (let i = 1; i <= limit; i++) {
    const tile = tiles[tiles.length - i];
    ...
}

After fix (same 9-participant case) :

Number of rows: 2
tilesLength: 9
noOfTilesToCheck: 4
limit: 4

i: 1 → tileIndex: 8
i: 2 → tileIndex: 7
i: 3 → tileIndex: 6
i: 4 → tileIndex: 5 

Exactly 4 tiles checked. No over-iteration. No negative indices. Safe and correct.

If helpful, I can also attach screenshots of the console logs showing the reproduction case and the behavior after applying the fix.

@Harxhit
Copy link
Author

Harxhit commented Mar 13, 2026

But how did you test it with the UI, what settings did you change to be able to reproduce the issue? How do you test it was fixed?

@Harxhit Harxhit closed this Mar 13, 2026
@Harxhit Harxhit reopened this Mar 13, 2026
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.

3 participants