Bugs item #1433877, was opened at 2006-02-17 16:29 Message generated for change (Comment added) made by qbarnes You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1433877&group_id=5470
Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Extension Modules Group: Python 2.4 Status: Open Resolution: None Priority: 9 Submitted By: Quentin Barnes (qbarnes) Assigned to: Thomas Wouters (twouters) Summary: string parameter to ioctl not null terminated, includes fix Initial Comment: I ran across this problem in Python 2.3.3 and see it is still there in 2.4.2. When running the test_pty.py test case, I would get intermittant failures depending on how the test was invoked or the compiler flags used on Solaris. Trussing the interpreter while running the test revealed: ioctl(4, I_PUSH, "ptem\xff^T^F^H") Err#22 EINVAL There was garbage on the end of the string when it would fail. I tracked the problem back to fcntl_ioctl() in fcntlmodule.c. The string was not being NULL terminated, but relied on having zeroed gargage on the stack to work. I checked the source for 2.4.2 and noticed the same problem. Patch to fix the problem is attached. ---------------------------------------------------------------------- >Comment By: Quentin Barnes (qbarnes) Date: 2006-02-20 12:00 Message: Logged In: YES user_id=288734 I didn't write new code that causes this problem by calling ioctl with a string that needed to be null terminated. It was already in Python code itself. See Lib/pty.py for the code. I reworked the patch as discussed below and retested it. All built-in tests passed. I've attached it. I hae no idea of Python coding conventions or style. Hopefully I didn't violate them too badly. (This was weird. SF rejected the text above when I submitted it, but it took the patch file. The patch file shows up as "nobody". Resubmitting this text.) ---------------------------------------------------------------------- Comment By: Michael Hudson (mwh) Date: 2006-02-20 04:54 Message: Logged In: YES user_id=6656 Seeing as some of this is my code... Guido, the ioctl docstring *does* mention mutate_arg. I agree that the documentation should have been updated for 2.4 and 2.5... and the situation is a mess, yes, but one that I couldn't see a better way around when the feature was added (it was much discussed in the bug report at the time). It certainly never occurred to me that you might need/want to pass a NULL terminated string to ioctl. I don't think this is a priority 9 report. ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2006-02-19 23:04 Message: Logged In: YES user_id=6380 Sorry for the confusion. I was in part responding to an off-line discussion I had with Neal Norwitz, to which you weren't a party. You can ignore my comments about safety and crashing. The difference in marshalling Python data to C data for ioctl(), compared to the many other places where (indeed) this problem has been solved before, is that almost everywhere else, we *know* what the data is supposed to mean. C has many more data types than Python, and the choice of how to convert a string, or an int, to C data depends on what is expected by the receiver of the C data. Unfortunately for ioctl we don't know the required C data type because it's defined by the kernel module that handles the particular ioctl opcode. Some of these (like the one you apparently ran into when porting the pty code to Solaris) require a null-terminated string; others (like the classic tty ioctls) require a C structure, which in Python can be created by using the struct module. Both return Python strings. I tend to agree with your conclusion that we should extend the buffer to 1025 bytes and not bother null-terminating non-string data. Would you like to attempt another patch? ---------------------------------------------------------------------- Comment By: Quentin Barnes (qbarnes) Date: 2006-02-19 18:31 Message: Logged In: YES user_id=288734 I've practically never used python. I just found this bug in the interpreter while porting the tool to the current Solaris compiler release, so please keep that in mind about my lack of python knowledge. I also have no idea who's who as well in the python world so forgive me if I've stepped on anyone's toes. I don't follow the remark about "making ioctl 'safe'". I never said anything about making it "safe". It's about a bug. I also never said anything about the interpreter crashing. The interpreter never crashed. The pty test just simply failed since the module name passed to the OS's ioctl was corrupted. Are you sure you responded to the right bug report with these remarks? Anyways... This problem should be reduced to a classic data marshaling problem between python data space and C data space which should have already been addressed in many contexts many times before in the interpreter. How are python strings converted for use by C in similar situations? This problem I encountered is either a bug in the code that passed the string to the ioctl service by not putting an explicit '\0' on the end of the "ptem" string, or it is a bug in the fcntlmodule.c which should have done it for it. Which is it? If a python code isn't expected to put a literal null at the end of their string when passing to a C service, then ioctl needs to be fixed similar to the way in my patch. As for null-terminating other raw data, I don't see the point other than a defensive programming practice. The problem I ran across is limited to just a data marshaling problem. The patch only touched the case when the type of data passed to ioctl is known to be a string. As for the buffer vs. documentation, the buffer could be changed to 1025 for the string case, or the documentation could be updated to clarify that, unlike raw data, strings are limited to 1023 to leave room for the requisite null that conversion to a C string requires. I don't think anyone would care either way as long as it is qualified. Since python strings serve dual roles of being both raw data and being just textual containers, one can't assume that a string passed to ioctl is always meant to be just a textual string. Going the 1025 route is probably a 'better' approach due to python string's duality. ---------------------------------------------------------------------- Comment By: Guido van Rossum (gvanrossum) Date: 2006-02-19 14:03 Message: Logged In: YES user_id=6380 One problem with ioctl() is that the required length of the buffer is unknown unless you parse the operation code argument in a very platform- (and probably even kernel- and driver-) dependent way. Whether null-terminating it makes sense or not depends on the particular operation code. I don't think the patch makes ioctl "safe", and I'm not sure it even ought to be applied. A quick code review reveals a few more issues: - the docstring doesn't explain the optional "mutate_flag" argument (which BTW is a mess -- read the online docs -- why are we changing the default to true?) - the online docs ought to be updated for 2.4 and again for 2.5 -- they still speak of 2.4 in the future tense! Also, it's a bit strong to say this function is "identical" to fcntl() -- "similar" sounds more like it. - In the first branch of the function, this line: if (mutate_arg && (len < sizeof buf)) { ought to test (len <= sizeof buf) to match the test earlier; but probably better is to use if (mutate_arg && arg == buf) { - If it's important to null-terminate the data in the buffer when a string is passed in, shouldn't we null-terminate it also when a writable buffer-interface object is passed in? If not in the latter case, why if a string is passed? One could argue that a string with an explicit \0 (visible to Python code) at the end ought to be passed in if the semantics of the op code requires a null-terminated argument (which most ioctl op codes don't -- the typical argument is a C struct). - The proposed patch reduces the maximum buffer size to 1023, which violates the docs. (Yes the docs explicitly mention 1024.) - The best we can do seems to be: increase the buffer size to 1025; null-pad it in all cases; change the code for mutable buffers to test for sizeof buf - 1. This still leaves the case where the buffer isn't used because a mutable buffer is passed that's longer than 1024. Which suggests that we can't completely fix this -- it will always be possible to crash Python using this function by passing an op code that encodes a buffer length > 1024 while passing a shorter argument, so the internal buffer is used. I guess we could do the null-padding as long as we document it and document that when a mutable buffer is passed we don't guarantee it. ---------------------------------------------------------------------- Comment By: Neal Norwitz (nnorwitz) Date: 2006-02-18 01:14 Message: Logged In: YES user_id=33168 Thomas, could this have caused the flakiness that you just fixed with test_pty? This patch seems correct to me and I think it should be applied. (If you want I can do that, but I wanted you to see this first.) ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1433877&group_id=5470 _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com