BBS:      TELESC.NET.BR
Assunto:  Win32 debug heap assertion after reading a cached filter filter while
De:       Rob Swindell
Data:     Fri, 13 Mar 2026 18:52:45 -0700
-----------------------------------------------------------
https://gitlab.synchro.net/main/sbbs/-/issues/1099#note_8576

Tried to create a small C++ reproduction case for this issue, unsuccessfully.

Created xpdev/strlisttest.cpp:
```
#include "str_list.h"
#include "threadwrap.h"
#include "genwrap.h"
#include "dirwrap.h"

#include 
#include 
#include 

static char* process_findstr_item(size_t index, char *str, void* cbdata)
{
	SKIP_WHITESPACE(str);
	truncnl(str);
	return c_unescape_str(str);
}

str_list_t findstr_list(const char* fname)
{
	FILE*      fp;
	str_list_t list;

	if ((fp = fopen(fname, "r")) == NULL)
		return NULL;

	list = strListReadFile(fp, NULL, 1000);
	strListModifyEach(list, process_findstr_item, /* cbdata: */ NULL);
	printf("Read %s\n", fname);

	fclose(fp);

	return list;
}

class filterFile {
	public:
		filterFile() {
			pthread_mutex_init(&mutex, nullptr);
		}
		filterFile(const char* fname) : filterFile() {
			init(fname);
		}
		void init(const char* fname) {
			strlcpy(this->fname, fname, sizeof this->fname);
		}
		filterFile(const filterFile&) = delete;
		filterFile& operator=(const filterFile&) = delete;
		~filterFile() {
			strListFree(&list);
			pthread_mutex_destroy(&mutex);
		}
		void reset() {
			fread_count = 0;
			total_found = 0;
			timestamp = 0;
			lastftime_check = 0;
			strListFree(&list);
		}
		std::atomic fread_count{};
		std::atomic total_found{};
		time_t fchk_interval{1}; // seconds
		char fname[MAX_PATH + 1]{};
		bool listed(const char* str1, const char* str2 = nullptr, struct trash* details = nullptr) {
			bool result;
			time_t now = time(nullptr);
			if (fchk_interval) {
				pthread_mutex_lock(&mutex);
				if ((now - lastftime_check) >= fchk_interval) {
					lastftime_check = now;
					time_t latest = fdate(fname);
					if (latest > timestamp) {
						strListFree(&list);
						list = findstr_list(fname);
						timestamp = latest;
						++fread_count;
					}
				}
				result = false; //trash_in_list(str1, str2, list, details);
				pthread_mutex_unlock(&mutex);
			}
			if (result)
				++total_found;
			return result;
		}
	private:
		str_list_t list{};
		pthread_mutex_t mutex{};
		time_t lastftime_check{};
		time_t timestamp{};

};

filterFile filter;

void thread(void* arg) {
	filter.listed("");
}

int main(int argc, char ** argv) {

	for (int i = 1; i < argc; ++i) {
		filter.init(argv[i]);
		_beginthread(thread, 0, nullptr);
		SLEEP(2000);
		printf("Freeing list\n");
		filter.reset();
		printf("Done freeing list\n");
	}
	
	return 0;
}
```
Build it with this command-line replicating all the options to be sbbs.dll Win32-debug and adding address-sanitizer:
```
cl /GS /analyze- /W3 /Zc:wchar_t /Zi /Gm- /Od /Zc:inline /fp:precise /D "_DEBUG" /D "WIN32" /D "_LIB" /D "LINK_LIST_THREADSAFE" /D "WINVER=0x600" /D "_WIN32_WINNT=0x600" /D "HAS_INTTYPES_H" /D "HAS_STDINT_H" /D "XPDEV_THREAD_SAFE" /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "_VC80_UPGRADE=0x0600" /D "_MBCS" /errorReport:prompt /WX- /Zc:forScope /RTC1 /std:c17 /arch:IA32 /Gd /Oy- /MTd /FC /EHsc /nologo /diagnostics:column  /fsanitize=address -DHAS_STDINT_H strlisttest.cpp str_list.c genwrap.c xpprintf.c dirwrap.c threadwrap.c
```

The resulting executable runs just fine:
```
C:\sbbs\src\xpdev>strlisttest s:\sbbs\text\ip.can s:\sbbs\text\host.can s:\sbbs\text\ip.can s:\sbbs\text\host.can s:\sbbs\text\ip-silent.can
Read s:\sbbs\text\ip.can
Freeing list
Done freeing list
Read s:\sbbs\text\host.can
Freeing list
Done freeing list
Read s:\sbbs\text\ip.can
Freeing list
Done freeing list
Read s:\sbbs\text\host.can
Freeing list
Done freeing list
Read s:\sbbs\text\ip-silent.can
Freeing list
Done freeing list
```

I'm out of ideas for now.
n
---
  mSynchronetn  hgVertrauen n hHome of Synchronet n gh[vert/cvs/bbs].synchro.net

-----------------------------------------------------------
[Voltar]