Uncrash crashes TSE v4.50.14

15 views
Skip to first unread message

Carlo Hogeveen

unread,
Nov 29, 2025, 12:21:17 PM (8 days ago) Nov 29
to sem...@googlegroups.com

Warning:
My existing "Uncrash" extension makes GUI TSE 4.50.14 abort with a ""Dave" memory-error when you open an existing file.
A temporary work-around is to at start-up open a new file, use the Macro AutoLoad List menu to delete "Uncrash", and restart TSE.
I will investigate and report back.

Carlo



S.E. Mitchell

unread,
Nov 29, 2025, 1:32:25 PM (8 days ago) Nov 29
to sem...@googlegroups.com
It is a C runtime error on a call to the C library routine malloc().
I can repro it with:
e an-empty-file.txt -l uncrash

Wait a few seconds, and it crashes. I can reproduce it under the
debugger, but I'm not sure why malloc() crashes.

Below is the code leading up to the crash:
> --
>
> ---
> 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/001601dc6154%2491b07500%24b5115f00%24%40ecarlo.nl.
image.png

knud van eeden

unread,
Nov 29, 2025, 1:47:13 PM (8 days ago) Nov 29
to sem...@googlegroups.com
<ChatGPT>

What your screenshot shows is not “malloc being bad”, it’s the CRT’s heap code falling over because its internal bookkeeping has already been damaged.

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:

  1. Walks the heap metadata (free lists, block headers/footers).

  2. 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.


What I’d do next in the debugger

Since you can reproduce this under the debugger:

  1. 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.

  2. 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.

  3. 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.

  4. 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.


Bottom line

  • 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.

with friendly greetings
Knud van Eeden

knud van eeden

unread,
Nov 29, 2025, 2:02:32 PM (8 days ago) Nov 29
to sem...@googlegroups.com

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.


The likeliest culprit

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().


Quick workaround to test

The fastest way to check whether that’s really the culprit is to disable the WMIC bit and see if the crashes go away.

Option A – completely bypass the process check

  1. 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
  1. 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
  1. 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.


Option B – keep the logic, but avoid async 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.


Other small things I noticed (but unlikely to cause a C heap crash)

These are worth cleaning up but aren’t likely to be the direct cause of a malloc() blow-up:

  1. 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.

  2. 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.


How I’d move forward

  1. Try Option A (no call to get_running_tse_processes() at all).

    • If the crash vanishes: we’ve narrowed it to that function.

  2. 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.

  3. 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.



Carlo Hogeveen

unread,
Nov 30, 2025, 5:46:39 AM (7 days ago) Nov 30
to sem...@googlegroups.com

Sammy,

About my "Uncrash" extension aborting in TSE 4.50.14.
Below is a short demo macro, that runs OK in TSE v4.50.13, and aborts with a "Dave" memory-error in TSE 4.50.14.
Based on the read.me this makes me suspect that there might be a bug in the changed Dos() command as well.

Note:
I added TSE 4.50.14's root directory to one of my Window's PATH environment variables get around the suspected SearchPath() bug.
Normally and otherwise I have no TSE directories at all in either of my Windows PATH environment variables.

Carlo



#define DOS_ASYNC_CALL_FLAGS _DONT_PROMPT_|_DONT_CLEAR_|_START_HIDDEN_|_RETURN_CODE_|_RUN_DETACHED_|_DONT_WAIT_

proc Main()
string cmd [MAXSTRINGLEN] = ''
string tmp_fqn [MAXSTRINGLEN] = ''

tmp_fqn = AddTrailingSlash(GetEnvStr('TMP')) + 'tse_uncrash_test.tmp'
cmd = 'wmic process list status > ' + QuotePath(tmp_fqn) + ' 2>&1'
EraseDiskFile(tmp_fqn)
Dos(cmd, DOS_ASYNC_CALL_FLAGS)
PurgeMacro(CurrMacroFilename())
end Main



Knud van Eeden

unread,
Nov 30, 2025, 4:14:12 PM (7 days ago) Nov 30
to sem...@googlegroups.com
image.png

image.png

Reproduced here too. 

with friendly greetings
Knud van Eeden

--

---
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.

Knud van Eeden

unread,
Nov 30, 2025, 4:18:28 PM (7 days ago) Nov 30
to sem...@googlegroups.com
Tried it with SYNC flags also, but still crashing:

image.png

image.png

Reply all
Reply to author
Forward
0 new messages