That little fragment:
00436533 8BD3 mov edx,ebx
00436535 83EE04 sub esi,4
00436538 8910 mov [eax],edx
0043653A 83E2FC and edx,FFFFFFFC
0043653D 8BCE mov ecx,esi
0043653F 03D0 add edx,eax
00436541 83C204 add edx,4
00436544 8D5901 lea ebx,[ecx+1]
00436547 891A mov [edx],ebx
00436549 8D5904 lea ebx,[ecx+4]
0043654C 891C0A --> mov [edx+ecx],ebx ; crash here
is typical heap-manager code manipulating a block’s header/footer and free-list links.
The faulting instruction writes to [edx+ecx]; if either edx or ecx has already been corrupted, that becomes an invalid address and you get the “Dave memory error” / access violation you’re seeing.
Malloc only does two things that can crash like this:
Walks the heap metadata (free lists, block headers/footers).
Writes new metadata into a block it’s (re)using.
If that metadata was trashed earlier, the very next malloc() (or free(), realloc()) eventually hits an impossible pointer and dies inside the RTL, exactly like in your screenshot.
So the real bug is almost certainly one of:
Writing past the end (or before the start) of some heap buffer.
Using a pointer after its block was freed.
Freeing the same block twice.
Overwriting heap control structures via a wild pointer.
Given that the problem appears only when -l uncrash is used and goes away if you remove that extension:
The corruption is almost surely caused by code in (or triggered by) Uncrash, not by the editor core.
Even if Uncrash itself is SAL, it may be driving some C-side API in a way the editor doesn’t expect (re-entrancy, bogus sizes/indices, etc.), and that in turn corrupts the heap.
Since you can reproduce this under the debugger:
Get the call stack at the crash.
See which function in the RTL this is (some _heap_alloc, _nh_malloc, etc.) and more importantly: which editor routine (or Uncrash hook) called into it.
Set a breakpoint on malloc/free earlier.
When it breaks in malloc(size), check the requested size. If it’s insane (e.g. huge, or 0xCCCCCCCC, etc.), the caller already has a bad value.
Do the same for free(ptr) and watch for obviously bogus pointers.
Turn the crash address into a data watchpoint.
When it crashes, note the address inside [edx+ecx].
Restart, set a data breakpoint on that address being written.
The first write that touches it incorrectly (before malloc dies) is usually where the heap first gets corrupted.
Temporarily disable pieces of Uncrash.
If Uncrash has multiple hooks (on idle, on file open, on timer, etc.), comment them out / disable them one by one and recompile:
If the crash disappears when a certain hook is disabled, you’ve isolated the guilty path.
The malloc crash is just the symptom.
The cause is heap corruption done earlier, almost certainly by logic that Uncrash triggers (buffer overrun, bad pointer, or misuse of an editor API).
To fix it, you need to track backwards from this malloc into the caller(s) and find where the heap bookkeeping first goes wrong.
If you can share the C source (or SAL + any C glue) that Uncrash uses, I can go through it and point out the most likely corruptors.
Thanks for sending the full UnCrash.s – that helps a lot.
Short version: I don’t see anything in the SAL code that obviously scribbles over memory (no bad DLL prototypes, no wild buffer-ID use, etc.). The pattern you see – “Dave” C-runtime malloc() blowing up after a short while – fits much better with:
“heap got corrupted earlier, malloc() is just the first poor soul that trips over it”
and the most suspicious place in this macro, especially with TSE 4.50.14, is the process-enumeration code run once at startup.
On first idle, idle() calls check_for_tse_crashes() (only once). That immediately calls:
running_tse_processes_id = get_running_tse_processes()
get_running_tse_processes() does this:
running_tse_processes_id = CreateTempBuffer()
ChangeCurrFilename(MY_MACRO_NAME + ':RunningTseProcesses',
CHANGE_CURR_FILENAME_FLAGS)
tmp_id = CreateTempBuffer()
tmp_fqn = session_uncrash_dir + SLASH + 'tmp.log'
cmd = 'wmic process list status > ' + QuotePath(tmp_fqn) + ' 2>&1'
EraseDiskFile(tmp_fqn)
Dos(cmd, DOS_ASYNC_CALL_FLAGS) // <-- runs WMIC *detached* and *don’t wait*
...
if wait_for_file(tmp_fqn, 90) // then we poll the temp file
LoadBuffer(tmp_fqn)
...
endif
That combination – asynchronous Dos() with _RUN_DETACHED_|_DONT_WAIT_ plus WMIC plus then polling the file – is very “exotic” and depends heavily on TSE’s internal Dos/heap implementation. It worked with earlier versions, but if 4.50.14 changed the way detached processes or their I/O buffers are handled, this is exactly the kind of thing that can silently corrupt the heap, and then some later malloc() explodes.
And your repro matches that: start TSE with UnCrash autoloaded, wait a short while → idle kicks in → check_for_tse_crashes() → get_running_tse_processes() → somewhere later the heap dies inside malloc().
The fastest way to check whether that’s really the culprit is to disable the WMIC bit and see if the crashes go away.
Change check_for_tse_crashes() so it doesn’t call get_running_tse_processes() at all:
proc check_for_tse_crashes()
integer crashes_id = 0
integer day_journals_handle = 0
...
integer running_tse_processes_id = 0 // <- initialize to 0, no call
// running_tse_processes_id = get_running_tse_processes() <-- comment this out
At the end of check_for_tse_crashes() wrap the abandon in a guard:
...
AbandonFile(day_journal_id)
AbandonFile(crashes_id)
if running_tse_processes_id // only if non-zero
AbandonFile(running_tse_processes_id)
endif
endif
checked_for_tse_crashes = TRUE
end check_for_tse_crashes
Recompile, put it on the AutoLoad list, and try your:
e an-empty-file.txt -l uncrash
If the crash disappears, we’ve nailed the problematic region.
This change only removes the “don’t treat currently running TSE sessions as crashed” refinement; everything else (backups, restore logic) still works. For most usage that’s an acceptable trade-off.
Dos()If you want to keep the “skip running TSE processes” logic, but still test whether async Dos() is the trigger, change just the one line in get_running_tse_processes():
// old:
Dos(cmd, DOS_ASYNC_CALL_FLAGS)
// try this instead:
Dos(cmd, DOS_SYNC_CALL_FLAGS)
Now WMIC runs synchronously; you don’t need _RUN_DETACHED_ / _DONT_WAIT_. This call only happens once per TSE startup, so a short pause isn’t terrible. If this also fixes the crash, it’s very strong evidence that the 4.50.14 change is in the async-process/heap area, not in the rest of UnCrash.
These are worth cleaning up but aren’t likely to be the direct cause of a malloc() blow-up:
GetProfileStr value ignored in get_uncrash_dir():
if cfg_uncrash_dir == ''
GetProfileStr(VARNAME_CFG_SECTION, 'Folder', '')
if cfg_uncrash_dir == ''
should almost certainly be:
if cfg_uncrash_dir == ''
cfg_uncrash_dir = GetProfileStr(VARNAME_CFG_SECTION, 'Folder', '')
if cfg_uncrash_dir == ''
Right now the configured folder in tse.ini is silently ignored.
A couple of very minor textual glitches in the copy I see (e.g. in delete_old_sessions and is_date) look like parsing artefacts, not real bugs – your source obviously compiles, so I’ll assume those are just from the viewer.
Try Option A (no call to get_running_tse_processes() at all).
If the crash vanishes: we’ve narrowed it to that function.
Then, if you care about the “skip running TSE” feature:
Restore the call, but switch to DOS_SYNC_CALL_FLAGS (Option B).
If that’s also stable, that’s probably the final fix.
If crashes still happen, the next suspects would be:
rm_dir() using async Dos() with /s /q.
Or a newly introduced bug in 4.50.14 in the hook/idle system that UnCrash happens to tickle.
If you test those two variants and tell me what happens (“still crashes / no crashes”), I can then suggest a much more precise patch (or a minimal test macro) that the creator of the uncrash.s program can use to report a reproducible TSE core bug.


--
---
You received this message because you are subscribed to the Google Groups "SemWare TSE Pro text editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to semware+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/semware/001401dc61e6%24999a2260%24ccce6720%24%40ecarlo.nl.

