[PATCH 2/2] comctl32/listview: Fix LVM_GETITEM for out-of-range iSubItem case.

Nikolay Sivov nsivov at codeweavers.com
Wed Dec 4 03:55:56 CST 2019


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=44842
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/comctl32/listview.c       | 41 ++++++++---------
 dlls/comctl32/tests/listview.c | 81 +++++++++++++++++++++++++++++++++-
 2 files changed, 99 insertions(+), 23 deletions(-)

diff --git a/dlls/comctl32/listview.c b/dlls/comctl32/listview.c
index acf6f31fe6..eb42526f57 100644
--- a/dlls/comctl32/listview.c
+++ b/dlls/comctl32/listview.c
@@ -6674,11 +6674,11 @@ static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImage
 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
 {
     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
+    BOOL is_subitem_invalid = FALSE;
     NMLVDISPINFOW dispInfo;
     ITEM_INFO *lpItem;
     ITEMHDR* pItemHdr;
     HDPA hdpaSubItems;
-    INT isubitem;
 
     TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
 
@@ -6688,10 +6688,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     if (lpLVItem->mask == 0) return TRUE;
     TRACE("mask=%x\n", lpLVItem->mask);
 
-    /* make a local copy */
-    isubitem = lpLVItem->iSubItem;
-
-    if (isubitem && (lpLVItem->mask & LVIF_STATE))
+    if (lpLVItem->iSubItem && (lpLVItem->mask & LVIF_STATE))
         lpLVItem->state = 0;
 
     /* a quick optimization if all we're asked is the focus state
@@ -6701,7 +6698,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
 	 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
     {
         lpLVItem->state = 0;
-        if (infoPtr->nFocusedItem == lpLVItem->iItem && isubitem == 0)
+        if (infoPtr->nFocusedItem == lpLVItem->iItem && !lpLVItem->iSubItem)
             lpLVItem->state |= LVIS_FOCUSED;
         return TRUE;
     }
@@ -6723,7 +6720,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
 	     *       depend on the uninitialized fields being 0 */
 	    dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
 	    dispInfo.item.iItem = lpLVItem->iItem;
-	    dispInfo.item.iSubItem = isubitem;
+	    dispInfo.item.iSubItem = lpLVItem->iSubItem;
 	    if (lpLVItem->mask & LVIF_TEXT)
 	    {
 		if (lpLVItem->mask & LVIF_NORECOMPUTE)
@@ -6770,7 +6767,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
 	    lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
 
 	/* we store only a little state, so if we're not asked, we're done */
-	if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
+	if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
 
 	/* if focus is handled by us, report it */
 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
@@ -6796,21 +6793,22 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     lpItem = DPA_GetPtr(hdpaSubItems, 0);
     assert (lpItem);
 
-    if (isubitem)
+    if (lpLVItem->iSubItem)
     {
-        SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
-        pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
-        if (!lpSubItem)
+        SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
+        if (lpSubItem)
+            pItemHdr = &lpSubItem->hdr;
+        else
         {
-            WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
-            isubitem = 0;
+            pItemHdr = &callbackHdr;
+            is_subitem_invalid = TRUE;
         }
     }
     else
 	pItemHdr = &lpItem->hdr;
 
     /* Do we need to query the state from the app? */
-    if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
+    if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && (!lpLVItem->iSubItem || is_subitem_invalid))
     {
 	dispInfo.item.mask |= LVIF_STATE;
 	dispInfo.item.stateMask = infoPtr->uCallbackMask;
@@ -6818,15 +6816,14 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
   
     /* Do we need to enquire about the image? */
     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
-        (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
+        (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
     {
 	dispInfo.item.mask |= LVIF_IMAGE;
         dispInfo.item.iImage = I_IMAGECALLBACK;
     }
 
     /* Only items support indentation */
-    if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
-        (isubitem == 0))
+    if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && !lpLVItem->iSubItem)
     {
         dispInfo.item.mask |= LVIF_INDENT;
         dispInfo.item.iIndent = I_INDENTCALLBACK;
@@ -6847,14 +6844,14 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     if (dispInfo.item.mask)
     {
 	dispInfo.item.iItem = lpLVItem->iItem;
-	dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
+	dispInfo.item.iSubItem = lpLVItem->iSubItem;
 	dispInfo.item.lParam = lpItem->lParam;
 	notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
 	TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
     }
 
     /* we should not store values for subitems */
-    if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
+    if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
 
     /* Now, handle the iImage field */
     if (dispInfo.item.mask & LVIF_IMAGE)
@@ -6865,7 +6862,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     }
     else if (lpLVItem->mask & LVIF_IMAGE)
     {
-        if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
+        if (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
             lpLVItem->iImage = pItemHdr->iImage;
         else
             lpLVItem->iImage = 0;
@@ -6897,7 +6894,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
 	lpLVItem->lParam = lpItem->lParam;
 
     /* if this is a subitem, we're done */
-    if (isubitem) return TRUE;
+    if (lpLVItem->iSubItem) return TRUE;
 
     /* ... the state field (this one is different due to uCallbackmask) */
     if (lpLVItem->mask & LVIF_STATE)
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c
index dc923c0b6e..87f644abd1 100644
--- a/dlls/comctl32/tests/listview.c
+++ b/dlls/comctl32/tests/listview.c
@@ -956,6 +956,37 @@ static void test_images(void)
     ok(EqualRect(&r1, &r2), "rectangle should be the same\n");
 
     DestroyWindow(hwnd);
+
+    /* I_IMAGECALLBACK set for item, try to get image with invalid subitem. */
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "Failed to create listview.\n");
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_IMAGE;
+    item.iImage = I_IMAGECALLBACK;
+    r = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+    ok(!r, "Failed to insert item.\n");
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_IMAGE;
+    r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(r, "Failed to get item.\n");
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq, "get image dispinfo 1", FALSE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_IMAGE;
+    item.iSubItem = 1;
+    r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(r, "Failed to get item.\n");
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "get image dispinfo 2", FALSE);
+
+    DestroyWindow(hwnd);
 }
 
 static void test_checkboxes(void)
@@ -4551,6 +4582,17 @@ static void test_indentation(void)
     ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
                 "get indent dispinfo", FALSE);
 
+    /* Ask for iIndent with invalid subitem. */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_INDENT;
+    item.iSubItem = 1;
+    r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(r, "Failed to get item.\n");
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "get indent dispinfo 2", FALSE);
+
     DestroyWindow(hwnd);
 }
 
@@ -6058,6 +6100,44 @@ static void test_callback_mask(void)
     mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0);
     ok(mask == ~0u, "got 0x%08x\n", mask);
 
+    /* Ask for state, invalid subitem. */
+    insert_item(hwnd, 0);
+
+    ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, LVIS_FOCUSED, 0);
+    ok(ret, "Failed to set callback mask.\n");
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.iSubItem = 1;
+    item.mask = LVIF_STATE;
+    item.stateMask = LVIS_SELECTED;
+    ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(ret, "Failed to get item data.\n");
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_STATE;
+    item.stateMask = LVIS_SELECTED;
+    ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(ret, "Failed to get item data.\n");
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, callback mask/invalid subitem 1", TRUE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    memset(&g_itema, 0, sizeof(g_itema));
+    item.iSubItem = 1;
+    item.mask = LVIF_STATE;
+    item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
+    ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(ret, "Failed to get item data.\n");
+    ok(g_itema.iSubItem == 1, "Unexpected LVN_DISPINFO subitem %d.\n", g_itema.iSubItem);
+    ok(g_itema.stateMask == LVIS_FOCUSED, "Unexpected state mask %#x.\n", g_itema.stateMask);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
+            "parent seq, callback mask/invalid subitem 2", FALSE);
+
     DestroyWindow(hwnd);
 
     /* LVS_OWNERDATA, mask LVIS_FOCUSED */
@@ -6271,7 +6351,6 @@ static void test_state_image(void)
         item.iSubItem = 2;
         r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
         ok(r, "Failed to get subitem state.\n");
-    todo_wine
         ok(item.state == 0, "Unexpected state %#x.\n", item.state);
 
         item.mask = LVIF_TEXT;
-- 
2.24.0




More information about the wine-devel mailing list