Re: tinytds based on freetds crashing
Frediano Ziglio <[email protected]>
| Newsgroups | gmane.comp.db.tds.freetds |
|---|---|
| Message-ID | <[email protected]> |
Il giorno 04/apr/2012, alle ore 22:18, "James K. Lowden" <[email protected]> ha scritto: > On Wed, 4 Apr 2012 13:00:05 +0200 > Krzysztof Chodak <[email protected]> wrote: > >>> Here it is: TDSDUMP log with a (ruby/tinytds/freetds) crash at the >>> end >> >> list does not like zip attachments so file is placed here: >> https://docs.google.com/open?id=0B8Pbr6Jr1eaCZnFCUDk3LWxTYXVncFpCaUdfdjhIdw > > (What message did you get posting a zip file? It should be > permitted provided it's under 75 KB.) > > The TDSDUMP log clarifies several points. > > FreeTDS 0.91 > TDS 7.1 > Windows (log file is on C: drive) > client charset is UTF-8 (unusual but OK) > > The trouble starts when the server disconnects unexpectedly. It ends > when unbounded recursion caused by the application's error handler > consumes the stack and crashes the process. > > Why the server disconnected isn't clear or even certain. All that we > know is that the last write returned "Connection reset by peer", which > normally indicates the connection was closed by the remote. > > The last packet sent is on line 81590 of the log file > > SELECT TOP (1) [users].* FROM [users] > WHERE [users].[login] = N''bradoscb'' > > Immediately beneath the dumped packet we see > > net.c:667:send(2) failed: 10054 > (WSAECONNRESET: Connection reset by peer.) > > which tells us the library detected the OS error. Then > > util.c:331:tdserror(03a04588, 03fe49b0, 20006, 10054) > dblib.c:7929:dbperror(03d32008, 20006, 10054) > dblib.c:7981:20006: "Write to the server failed" > > per normal, then > > dblib.c:5780:dbgetuserdata(03d32008) > dblib.c:5780:dbgetuserdata(03d32008) > dblib.c:4880:dbdead(03d32008) [alive] > > wrong. A disconnected session is anything but alive. > > dblib.c:4639:dbsqlok(03d32008) > dblib.c:4669:dbsqlok() not done, calling tds_process_tokens() > > db-lib still thinks the session is up ... > > token.c:540:tds_process_tokens(03fe49b0, 00226220, 0022621c, 0x6914) > util.c:156:Changed query state from PENDING to READING > util.c:331:tdserror(03a04588, 03fe49b0, 20004, 10054) > dblib.c:7929:dbperror(03d32008, 20004, 10054) > dblib.c:7981:20004: "Read from the server failed" > > but the read failed ... > > dblib.c:5780:dbgetuserdata(03d32008) > dblib.c:5780:dbgetuserdata(03d32008) > dblib.c:4880:dbdead(03d32008) [alive] > dblib.c:4639:dbsqlok(03d32008) > dblib.c:4669:dbsqlok() not done, calling tds_process_tokens() > > and we enter an infinite loop. > > Analysis > --------- > > Windows returns a valid error code WSAECONNRESET. This is reported to > the log in tds/net.c:tds_goodwrite() on line 667. That logic closes > the socket and returns -1 to tds_write_packet(). > > How did we get there? The log shows > > dblib.c:6862:dbsqlsend(03d32008) > write.c:168:tds_put_string wrote 194 bytes > > tds_put_string() calls > tds_put_n() calls > tds_write_packet > > tds_write_packet() returrns TDS_FAIL to tds_put_n() > > which discards the return code. :-( > > Propogating the error code is not the only way error information is > provided to db-lib from libtds. tds_goodwrite() also calls tdserror > (), which calls the client's error handler. We see that in the log, too > > util.c:331:tdserror(03a04588, 03fe49b0, 20006, 10054) > dblib.c:7929:dbperror(03d32008, 20006, 10054) > dblib.c:7981:20006: "Write to the server failed" > > What we do *not* see in the log is the product of the next line in > tdserror() > > tdsdump_log(TDS_DBG_FUNC, > "tdserror: client library returned %s(%d)\n", retname(rc), rc); > > The missing message in the log indicates the error handler is Charlie on > the MTA: it Never Returned. > > It would appear -- whether by design or not -- that the > client-installed error handler makes db-lib calls, in particular > dbsqlok(). Don't do that. It engenders infinite recursion just > as evinced by the log: > > dblib.c:4669:dbsqlok() not done, calling tds_process_tokens() > token.c:540:tds_process_tokens(03fe49b0, 00226220, 0022621c, 0x6914) > util.c:331:tdserror(03a04588, 03fe49b0, 20004, 10054) > dblib.c:7929:dbperror(03d32008, 20004, 10054) > dblib.c:7981:20004: "Read from the server failed" > # no tdserror message here > # to indicate the handler's return code > ... > dblib.c:4639:dbsqlok(03d32008) # from within handler > dblib.c:4669:dbsqlok() not done, calling tds_process_tokens() > > (It looks like an infinite loop, but it's infinite recursion. > Computationally equivalent constructs, just one is more familar to C > programmers than the other.) > > Incidentally, this would also explain why dbdead() reports "[active]". > In tds_goodread(), the socket isn't closed until after tdserror() > returns. Because the socket is still open within the recursion, all > calls to dbdead() continue to find the socket open and it reports > "active". > Fixed in both 0.91 and master! > Give your error handler a nickle and let it return. Otherwise it will > eventually devour the stack along with your process. > > --jkl > Frediano