http://bugs.winehq.org/show_bug.cgi?id=13319
Summary: In dlls/user32/edit.c EDIT_EM_ReplaceSel Clobbers Important Var When Buffer Overflows Product: Wine Version: unspecified Platform: All OS/Version: All Status: UNCONFIRMED Severity: normal Priority: P2 Component: user32 AssignedTo: wine-bugs@winehq.org ReportedBy: bsmith@sudleyplace.com
Please don't hate me, but I don't have git or anything like it installed to make a formal patch, but I have benefited greatly from your work, so I'd like to repay the effort by reporting a bug even though I realize it's not in the correct format.
In a Windows app, I am using edit.c as a replacement for the EDIT control in Windows. I don't use any of the other WineHQ files.
Here's a short description of the bug:
The handler EDIT_EM_ReplaceSel misbehaves when an insertion triggers a buffer overflow. The code correctly calls EDIT_NOTIFY_PARENT(es, EN_MAXTEXT), but shortly thereafter clobbers an important variable (strl) by using it instead of a temporary.
The relevant OLD code in EDIT_EM_ReplaceSel is as follows: -------------------------------------------------------------- if ((honor_limit) && (size > es->buffer_limit)) { EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); /* Buffer limit can be smaller than the actual length of text in combobox */ if (es->buffer_limit < (tl - (e-s))) strl = 0; else strl = es->buffer_limit - (tl - (e-s)); }
if (!EDIT_MakeFit(es, tl - (e - s) + strl)) return; -------------------------------------------------------------- the NEW code is as follows: -------------------------------------------------------------- if ((honor_limit) && (size > es->buffer_limit)) { EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); /* Buffer limit can be smaller than the actual length of text in combobox */ if (es->buffer_limit < (tl - (e-s))) strl2 = 0; else strl2 = es->buffer_limit - (tl - (e-s)); } else strl2 = strl;
if (!EDIT_MakeFit(es, tl - (e - s) + strl2)) return; -------------------------------------------------------------- The calculation inside the honor_limit bracket of the value to use with the call to EDIT_MakeFit uses strl as if it were a temp var. This variable actually holds strlenW (lpsz_replace) and is used in later code as if it still had the original value. Using a (new) variable strl2 solves that problem -- this variable is declared as a UINT in the prologue. Perhaps you would prefer a name different from strl2 to better reflect its temporary nature.
If you agree with the above analysis, I would greatly appreciate it if someone would make this into a patch and take it from there. I have make the above changes in my copy of edit.c and it works just fine when the buffer overflows which is how I stumbled on this bug in the first place.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #1 from Lei Zhang thestig@google.com 2008-05-20 11:53:57 --- Created an attachment (id=13203) --> (http://bugs.winehq.org/attachment.cgi?id=13203) edit.c changes in diff format
Can you attach some test code that triggers the problem you're trying to fix?
http://bugs.winehq.org/show_bug.cgi?id=13319
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |patch
http://bugs.winehq.org/show_bug.cgi?id=13319
Bob Smith bsmith@sudleyplace.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |bsmith@sudleyplace.com
--- Comment #2 from Bob Smith bsmith@sudleyplace.com 2008-05-20 14:52:09 --- Thanks so much for the patch. You'll need to modify it to include defining the new temp var. I inserted a UINT strl2; in the prologue.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #3 from Juan Lang juan_lang@yahoo.com 2008-05-22 10:01:25 --- Could you attach a short bit of code that triggers the overflow and shows how it should work?
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #4 from Luke Bratch l_bratch@yahoo.co.uk 2008-05-28 20:06:29 --- Bob, please do not reply to wine-bugs, post in Bugzilla instead. Email read:
"Attached is a C file which should demonstrate the problem. You'll need to adapt it to your environment as I use EDIT.C standalone. In particular, you'll need to change the value of LECWNDCLASS to the correct class name.
Perhaps the best way to see what's happening is to insert lines into the EDIT.C function EDIT_EM_ReplaceSel:
Just before the lines
for (i = 0 , p = es->text + s ; i < strl ; i++) p[i] = lpsz_replace[i];
insert the lines
if (strl > strlenW (lpsz_replace)) MessageBoxW (es->hwndParent, L"Indexing out of bounds", L"EDIT_EM_ReplaceSel", MB_OK | MB_ICONEXCLAMATION);
Obviously, when strl retains its original value of strlenW(lpsz_replace), the FOR loop indexes lpsz_replace from start to finish. Thus, if the MessageBoxW statement triggers, we're about to index lpsz_replace beyond its bounds.
-----------------------------------------------------
On related point, when the buffer overflows, the parent gets one shot to set new limits. Moreover, it might have no way of knowing how close it is to the buffer limit, so it's hard to decide by how much the limit should be increased. It occurred to me that a small change in the code could resolve this problem:
Just after the line
if ((honor_limit) && (size > es->buffer_limit)) {
replace the call to EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); with
while (size > es->buffer_limit) { UINT oldLimit = es->buffer_limit; EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); // If the new limit didn't increase, don't try again if (oldLimit >= es->buffer_limit) break; }
This way, the caller gets notified until the buffer limit is big enough or the buffer limit doesn't change.
-----------------------------------------------------
FWIW, in EDIT.C I also found three typos/bugs.
#1:
old
ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE);
new
ExStyle = GetWindowLongW(es->hwndSelf, GWL_EXSTYLE);
That is, GWL_EXSTYLE is a long, not a long ptr.
#2:
old
hBrush = (HBRUSH) GetClassLongW (es->hwndSelf, GCL_HBRBACKGROUND);
new
hBrush = (HBRUSH) GetClassLongPtrW (es->hwndSelf, GCL_HBRBACKGROUND);
That is, GCL_HBACKGROUND is a ptr, not a long.
#3:
old
MAKEWPARAM(GetWindowLongPtrW((es->hwndSelf),GWLP_ID), wNotifyCode), \
new
MAKEWPARAM(GetWindowLongW((es->hwndSelf),GWLP_ID), wNotifyCode), \
I thought that the GWL_ID was an integer (although it often is cast with (HMENU)). Nonetheless, MAKEWPARAM is expecting two four-byte values. Under _WIN64, the difference is important."
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #5 from Luke Bratch l_bratch@yahoo.co.uk 2008-05-28 20:06:51 --- Created an attachment (id=13449) --> (http://bugs.winehq.org/attachment.cgi?id=13449) Bob's attachment
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #6 from Bob Smith bsmith@sudleyplace.com 2008-05-28 22:02:13 --- (In reply to comment #4)
Bob, please do not reply to wine-bugs, post in Bugzilla instead.
Sorry, I got an email asking me to post a sample program and I replied to that message. Even the message you sent to me asking me not to reply to wine-bugs has a from address of wine-bugs@winehq.org, so it's easy to see how this can happen.
Thanks for the heads up -- I understand the drill now.
http://bugs.winehq.org/show_bug.cgi?id=13319
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |testcase
--- Comment #7 from Austin English austinenglish@gmail.com 2008-05-29 11:39:01 --- Thanks for the testcase! Any chance of porting it to the conformance suite?
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #8 from Lei Zhang thestig@google.com 2008-05-29 12:23:10 --- I compiled and ran your test program under wine. (Just change EditWndProcW to EditWndProc) and it doesn't look like strl ever becomes greater than strlenW(lpsz_replace).
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #9 from Bob Smith bsmith@sudleyplace.com 2008-05-29 12:30:15 --- (In reply to comment #8)
I compiled and ran your test program under wine. (Just change EditWndProcW to EditWndProc) and it doesn't look like strl ever becomes greater than strlenW(lpsz_replace).
1. I assume you also changed the value of LECWNDCLASS.
2. Did you insert the debug statements into EDIT.C?
3. The default buffer size is 30000 bytes in my configuration. What is it in yours?
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #10 from Lei Zhang thestig@google.com 2008-05-29 14:07:00 --- (In reply to comment #9)
- I assume you also changed the value of LECWNDCLASS.
No, what am I suppose to change it to?
- Did you insert the debug statements into EDIT.C?
Yes, I just did a printf() instead of MessageBoxW().
- The default buffer size is 30000 bytes in my configuration. What is it in
yours?
30000
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #11 from Bob Smith bsmith@sudleyplace.com 2008-05-29 15:23:35 --- (In reply to comment #10)
(In reply to comment #9)
- I assume you also changed the value of LECWNDCLASS.
No, what am I suppose to change it to?
- Did you insert the debug statements into EDIT.C?
Yes, I just did a printf() instead of MessageBoxW().
- The default buffer size is 30000 bytes in my configuration. What is it in
yours?
30000
Sorry, but the context I have is without the rest of WineHQ -- it has EDIT.C (pared down) only, so I must use a different class name for WineHQ Edit Controls because "EDIT" is in use by Windows. Thus I use the class name mentioned in LECWNDCLASS for a WineHQ Edit Control.
Change the #define for LECWNDCLASS to L"EDIT" and retry.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #12 from Bob Smith bsmith@sudleyplace.com 2008-05-29 15:33:07 --- Created an attachment (id=13465) --> (http://bugs.winehq.org/attachment.cgi?id=13465) Test Case With Fix to Window Class, Prototype, and no RegsiterClassExW
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #13 from Bob Smith bsmith@sudleyplace.com 2008-05-29 15:36:31 --- (In reply to comment #11)
(In reply to comment #10)
(In reply to comment #9)
- I assume you also changed the value of LECWNDCLASS.
No, what am I suppose to change it to?
Sorry, but the context I have is without the rest of WineHQ -- it has EDIT.C (pared down) only, so I must use a different class name for WineHQ Edit Controls because "EDIT" is in use by Windows. Thus I use the class name mentioned in LECWNDCLASS for a WineHQ Edit Control.
Change the #define for LECWNDCLASS to L"EDIT" and retry.
I misspoke about the changes. I've created another attachment which likely will trigger the bug in your context.
Please let me know if there is a problem as I don't have the same context as you folks do.
http://bugs.winehq.org/show_bug.cgi?id=13319
Lei Zhang thestig@google.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Attachment #13203|0 |1 is obsolete| |
--- Comment #14 from Lei Zhang thestig@google.com 2008-05-30 03:05:00 --- Created an attachment (id=13472) --> (http://bugs.winehq.org/attachment.cgi?id=13472) edit.c patch
Your patch breaks the user32 unit tests. I added a bit more debug output to your test app and ran it on Windows to figure out the proper behavior. I think this is it. I'll try to integrate your test into the test suite and then send in the patch. Meanwhile, please try out the patch in this attachment.
http://bugs.winehq.org/show_bug.cgi?id=13319
Lei Zhang thestig@google.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |NEW Ever Confirmed|0 |1
--- Comment #15 from Lei Zhang thestig@google.com 2008-05-30 03:05:43 --- and confirming
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #16 from Lei Zhang thestig@google.com 2008-05-30 03:07:41 --- (In reply to comment #4)
FWIW, in EDIT.C I also found three typos/bugs.
#1: ...
You should send a patch for them to wine-patches@winehq.org.
http://bugs.winehq.org/show_bug.cgi?id=13319
Bob Smith bsmith@sudleyplace.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Attachment #13465|0 |1 is obsolete| |
--- Comment #17 from Bob Smith bsmith@sudleyplace.com 2008-05-30 11:00:06 --- Created an attachment (id=13483) --> (http://bugs.winehq.org/attachment.cgi?id=13483) Added Tests To Detect Insufficient Data in The Edit Control
A previous proposed fix avoids the Indexing Out Of Bounds problem, but doesn't fill the buffer with all of the data. This test case checks for that occurrence.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #18 from Bob Smith bsmith@sudleyplace.com 2008-05-30 11:04:12 --- (In reply to comment #14)
Created an attachment (id=13472)
--> (http://bugs.winehq.org/attachment.cgi?id=13472) [details]
edit.c patch
Your patch breaks the user32 unit tests. I added a bit more debug output to your test app and ran it on Windows to figure out the proper behavior. I think this is it. I'll try to integrate your test into the test suite and then send in the patch. Meanwhile, please try out the patch in this attachment.
I'm not sure exactly what broke in unit tests (perhaps you can elaborate), but the proposed patch doesn't fill the buffer with all of the data.
I added more code to the test case to show the problem.
Fundamentally, any reassignment of <strl> which doesn't depend on strlenW(lpsz_replace) can't work.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #19 from Lei Zhang thestig@google.com 2008-05-30 11:57:46 ---
(In reply to comment #14) I'm not sure exactly what broke in unit tests (perhaps you can elaborate), but the proposed patch doesn't fill the buffer with all of the data.
I added more code to the test case to show the problem.
We have a set of conformance tests that run and pass under Windows. On Wine, they should all pass as well. When I applied your patch, some of the tests failed.
I ran your second test on Windows XP, and it reported "not enough data in the edit control", whereas on Wine, it says "just the right amount". With my patch, Wine has the same behavior as Windows.
Fundamentally, any reassignment of <strl> which doesn't depend on strlenW(lpsz_replace) can't work.
Sure it works. There's a limit to how much the edit control can hold. When lpsz_replace is too long, we need to set strl to something lower.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #20 from Lei Zhang thestig@google.com 2008-05-30 13:45:37 --- I sent a patch: http://www.winehq.org/pipermail/wine-patches/2008-May/055389.html
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #21 from Bob Smith bsmith@sudleyplace.com 2008-05-30 20:38:37 --- Let me try a different tack. From a static analysis point of view, here's why I don't understand how the patch can work and how the test case can succeed:
1. The patch saves the old buffer_limit, tells the parent about the overflow, but does not again examine the (possibly new) buffer limit to see if lpsz_replace now fits. I thought that the purpose of telling the parent about the overflow is to give her a chance to increase the limit so that the operation can continue successfully.
2. In the test case I wrote up, I start with an empty buffer (tl=e=s=0) with a limit of 30 and replace the (empty) selection with a string of length 40. In the patch, <strl> is set to old_limit - (tl - (e-s)) which is 30.
Subsequently, the for loop that copies lpsz_replace to the buffer goes from 0 to strl - 1. If strl is 30 and lpsz_replace is of length 40, then not all of lpsz_replace is copied to the buffer. Back in the test case, I check not only that the resulting length is 40, but also, if it is, that all 40 of the correct chars are there. Obviously we didn't copy all 40 chars to the buffer, so how can either of those checks succeed? ----------------------------------------------------- I think both of our fixes have flaws. One thinks the parent will always increase the buffer limit to be big enough, and the other thinks she never will. I like a combined fix as follows ----------------------------------------------------- if ((honor_limit) && (size > es->buffer_limit)) { EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); /* Buffer limit can be smaller than the actual length of text in combobox */ if (es->buffer_limit < (tl - (e-s))) strl = 0; else { UINT strl2;
strl2 = es->buffer_limit - (tl - (e-s)); strl = min (strl, strl2); } }
if (!EDIT_MakeFit(es, tl - (e - s) + strl)) return; ----------------------------------------------------- Essentially strl2 is the # available chars in the buffer (after the parent has had a chance to increase its limit) and strl is the # of chars we are allowed to insert.
http://bugs.winehq.org/show_bug.cgi?id=13319
Lei Zhang thestig@google.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |thestig@google.com
--- Comment #22 from Lei Zhang thestig@google.com 2008-08-19 16:52:26 --- I haven't had time to work on this. If you'd like to help, try writing test cases in dlls/user32/tests/edit.c for the cases you'd like to have fixed.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #23 from Austin English austinenglish@gmail.com 2009-02-16 02:12:45 --- Ping.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #24 from Bob Smith bsmith@sudleyplace.com 2009-02-16 09:48:51 --- (In reply to comment #23)
Ping.
I'm not sure how to proceed with this issue until we can agree on whether or not there is a bug. I tried to lay out my argument in comment #21, but as there have been no comments on its substance, I assume we still don't have agreement.
Also, perhaps I don't understand what you folks want as a test case, but I thought that my last attachment from 30 May 2008 was a test case. When I run it here, it demonstrates the bug.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #25 from Austin English austinenglish@gmail.com 2009-02-16 11:21:41 --- (In reply to comment #24)
(In reply to comment #23)
Ping.
I'm not sure how to proceed with this issue until we can agree on whether or not there is a bug. I tried to lay out my argument in comment #21, but as there have been no comments on its substance, I assume we still don't have agreement.
Also, perhaps I don't understand what you folks want as a test case, but I thought that my last attachment from 30 May 2008 was a test case. When I run it here, it demonstrates the bug.
Asking on wine-devel will probably get you more answers than on bugzilla.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #26 from Austin English austinenglish@gmail.com 2009-08-26 12:20:17 --- Still present.
http://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #27 from Austin English austinenglish@gmail.com 2010-05-19 16:14:14 --- Still present. Should probably at least get the testscases in as todo_wine's...
http://bugs.winehq.org/show_bug.cgi?id=13319
Austin English austinenglish@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Platform|All |Other OS/Version|All |other
--- Comment #28 from Austin English austinenglish@gmail.com 2012-02-23 15:19:56 CST --- Removing deprecated 'All' Platform/OS.
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #29 from Austin English austinenglish@gmail.com --- This is your friendly reminder that there has been no bug activity for 2 years. Is this still an issue in current (1.7.16 or newer) wine?
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #30 from Austin English austinenglish@gmail.com --- This is your friendly reminder that there has been no bug activity for over a year. Is this still an issue in current (1.7.51 or newer) wine?
https://bugs.winehq.org/show_bug.cgi?id=13319
super_man@post.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |super_man@post.com
--- Comment #31 from super_man@post.com --- I noticed that bug 37336 has crash location of EDIT_EM_ReplaceSel and it has a download to work on. It could be somehow related to this.
The patch still applies with some offsets against 1.9.2-git
https://bugs.winehq.org/show_bug.cgi?id=13319
Bo Simonsen bo@geekworld.dk changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |bo@geekworld.dk
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #32 from Bo Simonsen bo@geekworld.dk --- Anybody have any idea why the patch from Lei Z was never merged in? The bug is still present in 1.9.6, so I don't understand why the patch was not merged when it fixes the bug.
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #33 from Austin English austinenglish@gmail.com --- *** Bug 40379 has been marked as a duplicate of this bug. ***
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #34 from super_man@post.com --- If not directly dupe, at least related bug 40832.
https://bugs.winehq.org/show_bug.cgi?id=13319
winetest@luukku.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |winetest@luukku.com
--- Comment #35 from winetest@luukku.com --- (In reply to super_man from comment #34)
If not directly dupe, at least related bug 40832.
Also bug 23838 has issue with em_replacesel which includes a test patch.
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #36 from winetest@luukku.com --- Could someone take a look at some of these bugs? I quess this one has the best analyze? Getting this fixed could solve multiple other user32 bugs already mentioned here.
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #37 from winetest@luukku.com --- even bug 40832 has the same crash location.
https://bugs.winehq.org/show_bug.cgi?id=13319
Roman Pisl rpisl@seznam.cz changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |rpisl@seznam.cz
--- Comment #38 from Roman Pisl rpisl@seznam.cz --- Bug 23838 seems to be a duplicate. I already sent a simple patch to wine-patches.
Bug 40832 has a same crash location but different cause.
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #39 from winetest@luukku.com --- bug 37336 could be also be infected by this bug. The bug report doesnt offer the same version of the program as report time, but the newer version I just tried has the same crash location when trying to save and it gets fixed with your patch, but the saving still doesnt work with the program, could be a different problem. The console output indicates so.
https://bugs.winehq.org/show_bug.cgi?id=13319
--- Comment #40 from Roman Pisl rpisl@seznam.cz --- This bug should be fixed by
http://source.winehq.org/git/wine.git/commit/9de8ea75645d7092f888ddd7572f352...
https://bugs.winehq.org/show_bug.cgi?id=13319
winetest@luukku.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |00cpxxx@gmail.com, | |austinenglish@gmail.com, | |dimesio@earthlink.net
https://bugs.winehq.org/show_bug.cgi?id=13319
Bruno Jesus 00cpxxx@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Resolution|--- |FIXED Status|NEW |RESOLVED
--- Comment #41 from Bruno Jesus 00cpxxx@gmail.com --- (In reply to Roman Pisl from comment #40)
This bug should be fixed by
http://source.winehq.org/git/wine.git/commit/ 9de8ea75645d7092f888ddd7572f35204e672757
Testing using the testcase works with or without that patch, this bug is fixed but it was something else.
https://bugs.winehq.org/show_bug.cgi?id=13319
Alexandre Julliard julliard@winehq.org changed:
What |Removed |Added ---------------------------------------------------------------------------- Status|RESOLVED |CLOSED
--- Comment #42 from Alexandre Julliard julliard@winehq.org --- Closing bugs fixed in 2.0-rc1.
https://bugs.winehq.org/show_bug.cgi?id=13319
Nikolay Sivov bunglehead@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |joshuapcloud@gmail.com
--- Comment #43 from Nikolay Sivov bunglehead@gmail.com --- *** Bug 37336 has been marked as a duplicate of this bug. ***