ryanprichard
Joined 13 years ago
Joined 13 years ago
I was assuming I'd use genuine console handles, but recognize that they're associated with the special console, so divert their API calls.
Maybe the technique would make programs slower. I know that's a complaint people have about ConEmu.
There's a similar (4th?) technique I've considered -- instead of hooking every process attached to the console, hook APIs in the conhost process and reimplement the internal protocol. It should avoid the performance problems and confine the hackiness to one address space. The trouble is that the protocol is undocumented. A small change could break everything without warning, but MS could also redesign the whole thing, making a fix impossible.
I did report it to Microsoft before making it public. The reply was:
> Thank you for contacting the Microsoft Security Response Center (MSRC). I would suggest trying on a local VM to confirm BSOD. However, this currently is just a local DOS, which would would not be something we would investigate further. If you have any additional information on how this could be further used to exploit another user or a remote DOS, please let us know and we will look into it.
> For an in-depth discussion of what constitutes a product vulnerability please see the following:
> "Definition of a Security Vulnerability" <https://technet.microsoft.com/library/cc751383.aspx>
>Again, we appreciate your report.
Aside: About a year ago, I studied how the console handles worked (especially in relation to console attach/detach/alloc and CreateProcess), and I discovered a bunch of interesting things. For example, the "fake" handles were inherited regardless of the bInheritHandles parameter to CreateProcess, and an entire "ConsoleHandleSet" was inherited if a process attached to another process' console. Real handles in Win8 seemed to be either "bound" to a particular input/screen buffer or "unbound" so they worked with any attached console. It was possible to have a console open with no attached processes.
I tried to formalize the behavior I observed here: https://github.com/rprichard/win32-console-docs
In the course of testing, I found some neat bugs like:
- A small program that blue-screens Vista
- The inheritable flag on a console handle can be changed on any Windows version, except for Windows 7.
- CreateProcess has an undocumented(?) behavior where it duplicates stdio handles into the new process rather than inherit them. (The handle values will generally change.) Up until Windows 8.1, a stdio handle of INVALID_HANDLE_VALUE was duplicated into a true handle to the parent process. (Because INVALID_HANDLE_VALUE is actually the GetCurrentProcess() pseudo-handle, you see.)
- Assorted inconsistencies in Windows XP and WOW64 (probably fixed in newer OSs).
I'm not sure how much confidence I have in the spec. It is backed by a big test suite, but there are so many cases to consider.
[1] https://github.com/Microsoft/BashOnWindows/issues/111#issuec...
def foo(n: int):
pass
def bar():
foo("123")
It does detect this mismatch: def foo(n: int):
pass
foo("123")e.g.: Here's a simple implementation:
enum AnyKind { AnyInt, AnyDouble, AnyStr };
struct Any {
enum AnyKind kind;
union {
int i;
double d;
const char *s;
} u;
};
#define ANY(x) \
_Generic((x), \
int: (struct Any){ .kind = AnyInt, .u.i = (x) }, \
double: (struct Any){ .kind = AnyDouble, .u.d = (x) }, \
const char *: (struct Any){ .kind = AnyStr, .u.s = (x) })
int main() {
ANY(1);
ANY(1.0);
ANY("abc");
}
Clang gives errors like: error: initializing 'const char *' with an expression of incompatible type 'double'
This issue was discussed a bit on the GCC bugtracker: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64509Qualifiers might also be an issue. My impression so far is that every combination of differently qualified type is supposed to require a different _Generic association. (GCC 5.2 and Clang 3.7 differ here.) That would require a huge number of _Generic associations, but it might be possible to strip the qualifiers using a comma operator, _Generic( (0,(x)), ... ). A comma expression is not an lvalue, so perhaps it cannot be qualified. DR423[1] askes that question.
[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_423.htm
Still, that hasn't stopped various front-ends (including GCC and Clang) from adding them to their C++ dialects. My code compiles with Clang, but not GCC. GCC complains that it's not OK to take the address of the array compound literal.
FWIW, C99 makes it clear that the object has automatic storage duration lasting until the end of the enclosing block. i.e. There shouldn't be an issue with dangling pointers.
AFAICT, it could be done using _Generic and inline functions, if/when MSVC adds _Generic. I'd expect to see _Generic before seeing any of the GCC extensions, but I'm not aware of any commitment from MS to add it. There are some issues with _Generic and qualifiers that might slow its adoption. e.g. There's an open defect report regarding qualified rvalues (DR423), but even lvalues seem to behave differently between GCC 5.2.0 and Clang 3.7.1. GCC ignores an lvalue's qualifiers but still allows a "const int" branch. AFAICT, a const-qualified branch is never selected with GCC?
I'm wondering whether it's possible to use _Generic to require that the left and right operands have the same type (either before or after integral promotions). Qualifiers are an obvious nuisance, but the bigger problem is that even the unselected branches must still compile, and C does not have tuples.
- Statement expressions: e.g.: int x = ({ int y = 0; y; });
- If zero-args are allowed (e.g. bar()), then _args is 0-sized, which is also non-standard.
These GCC extensions are somewhat common. Clang has them, and (I think) EDG's frontend does too. MSVC does not have either of them, even in MSVC 2015.
That said, a compound literal might be a good way to remove the extension use, but as long as bar() returns void, the do-while(0) trick is also sufficient for replacing the statement expression.
I think this compound literal usage works:
#define bar(...) (bar( \
sizeof((const char*[]) { NULL, __VA_ARGS__ }) / sizeof(const char*) - 1, \
(const char*[]) { NULL, __VA_ARGS__ } + 1))
bar() results in trailing commas. My impression is that C99 allows them in array initializers. (I saw an example in the n1256 C draft.) I don't recall whether C89 also had them. __VA_ARGS__ is expanded twice, but I think that's OK, because sizeof() only evaluates its operand when it has VLA type, and it never will. This code will work in MSVC 2013 (if not earlier).I don't actually use the program on a daily basis, but I do pull it out on special occasions. Most recently I used it while tracking down https://github.com/atom/electron/issues/3380. It's helpful because Electron/Chromium is big and I'm unfamiliar with the code.
The indexer comes up with a globally-unique name for each "symbol" it indexes. This turned out to be a hard problem for things like templates and macros. The indexer names static functions by prepending a filename, e.g. "bio_ssl.c/ssl_write". For local variables, it also appends a file offset. With templates/macros, it can determine that a single identifier corresponds to arbitrarily many different symbols, and when you right-click that identifier in the GUI, it attempts to show them all in a popup menu, which fills the entire screen.
Windows doesn't have a good API for hosting a console--it's not like Unix, where a pty has a master end and a slave end. Trying to run a console program in mintty.exe (https://github.com/mintty/mintty/issues/56) or Cygwin SSHD fails for this reason. I wrote a tool, winpty, that makes a best-effort attempt to emulate a Unix pty master by scraping the console buffer, but it has some limitations, so I'm not sure Microsoft would want to use it. Maybe they would expand the console API?
The other interesting problems I found with that API were: (1) when editing an input line, EVENT_CONSOLE_CARET notifications were delayed, (2) if I hit ESC when editing an input line, the line would clear, but there'd be no update-region notification, and (3) it's impossible to distinguish between a call to ScrollConsoleScreenBuffer and the whole buffer scrolling because we've reached the end. ScrollConsoleScreenBuffer might scroll only part of the buffer.
winpty doesn't use the WinEvents API (yet), but it might be good enough to reduce polling when the console is idle.