v2.0.1 is the largest release in the history of Linux Malware Detect. It is a foundational rewrite of the scan, monitor, alert, and packaging stacks, retains complete backward compatibility for every public CLI flag and config variable, and closes 50+ user-reported issues.
Headline changes
- Native scan engine, no Perl dependency. HEX + CSIG merged into a single batch-scanned worker pass with parallel
grepworkers and micro-chunked processing (scan_hex_chunk_size). 43x faster on HEX-heavy workloads. Perl runtime requirement removed. - SHA-256 with hardware acceleration. New
scan_hashtype=auto|sha256|md5|bothselector; auto-detects SHA-NI on x86 / SHA2 on ARM and prefers SHA-256 transparently. Shipssha256v2.dat,custom.sha256.dat, and ClamAV.hsbintegration (gated on ClamAV ≥ 0.97). - Compound signatures (csig). Multi-pattern boolean matching with AND / OR / threshold subsigs, case-insensitive (
i:), wide UTF-16LE (w:), and bounded-gap wildcards ({N-M}). Newcsig.datandcustom.csig.dat;scan_csigconfig gate. - Scan lifecycle management. Foreground and background scans now expose
--kill,--pause/--unpause,--stop/--continue, and a redesigned-L/--list-activewith running, paused, stopped, and historical scans in one columnar view. Checkpoint resume iseval-free (printf -v +-coallowlist).scan.metarecords per-scan state;--maintenancerotates history, compresses sessions, and archives by month. - Inotify monitor redesign. Supervisor model replaces the legacy double-fork: graceful shutdown, crash recovery, session rotation, event filter, PID guard, and ClamAV cache integration. 12 long-standing monitor defects resolved at once.
monitor_paths_extra,digest_interval, anddigest_escalate_hitsadded. Closes #447, #454, #459. - TSV canonical session format. 11-field structured hit records (
#LMD_INDEX:v2, 14 fields total) replace plaintext session files.session_legacy_compat=autokeeps old reports readable. Powers JSON output and lifecycle list at O(1) cost per session. - JSON report output (schema 1.2).
--json-report [SCANID|list|newest]renders TSV sessions as JSON with a uniform{schema_version, scanner, host, reports[]}shape; the same envelope is used for per-scan and list output.started_epoch/stopped_epoch/completed_epochintegers for consumer-side sorting; legacy plaintext sessions emit the same shape with null fields. Closes #466. - Email alerts reimagined. Dual HTML + text format with a
{{TOKEN}}template engine, on-demand HTML rendering (no persisted.htmlartifacts), full SMTP relay support (smtp_relay,smtp_from,smtp_user,smtp_pass), and Outlook-compatible responsive layout. Closes #198, #265. - Slack, Telegram, Discord. Slack migrated off the deprecated
files.uploadAPI togetUploadURLExternal/completeUploadExternal(Block Kit templates). Telegram uses MarkdownV2 with proper/botprefix. Discord webhooks added for the first time (discord_alert,discord_webhook_url). Closes #387, #458, #461, #426. - Native YARA stage. YARA runs as an independent scan stage when
scan_yara=1(orauto), supportingyara(4.x) andyr(YARA-X). Custom rules viacustom.yaraandcustom.yara.d/drop-ins; compiled rules viayarac;--scan-listbatch scanning. Closes #239, #277, #392. - RPM and DEB packaging in-tree. Full RPM (el7/el8/el9) and DEB packaging under
pkg/with FHS-compliant layout, backward-compatible symlink farm (pkg/symlink-manifest), and adh_fixpermsoverride that preserves project modes.pkg-postinst.shshared across install methods. Closes #267. - Sub-library decomposition.
files/internals/functionssplit into 14 cohesivelmd_*.shmodules (config, init, clamav, sigs, engine, yara, quarantine, session, lifecycle, scan, hook, monitor, update, alert) sourced by almd.lib.shhub with_LMD_<STEM>_LOADEDsource guards. Vendored shared libraries:tlog_lib,elog_lib(structured event logging with audit trail),alert_lib,pkg_lib. - Hookscan API. Real-time scanning callable from ModSecurity, ProFTPD, Pure-FTPd, Exim, and a generic mode. Per-mode dispatch,
--list/--stdinbatch input, rate limiting, sig masking, on-hit escalation, deduped digests, and--report hookswith time/mode filters. - post_scan_hook. Configurable script execution after every scan completion. args/file/json output tiers, sync/async, SIGTERM/SIGKILL timeout,
min_hitsthreshold, scan-type filter, scan_start epoch in JSON. Closes #477. - Position-independent CLI. Modifier flags (
-x,-i,-hscan,-qd,-co,--format,--mailto) work in any order.-couses an in-memory parser. The-coallowlist (79 vars) and_safe_source_confclose the long-standing remote-config injection class. - CI on every push/PR. GitHub Actions smoke-test workflow, 9 OS matrix (CentOS 6/7, Rocky 8/9/10, Ubuntu 20.04/24.04, Debian 12, FreeBSD partial), 1080 BATS tests + 100-scenario UAT.
Incremental new features
scan_clamscanandscan_yaraacceptautofor runtime binary detection.--test-alert {scan|digest} {email|slack|telegram|discord}: send a synthetic alert through the real rendering pipeline, root-only.-v/--versionflag. Closes #451.- Independent 6-hourly signature update job via
/etc/cron.d/maldet-sigup, configurable viasigup_interval(set to 0 to disable). Pluscron.watchdogweekly fallback. cron.dailycPanel addon/subdomain detection via/etc/userdatadomainsand Bitrix panel detection. Closes #268, #381.- ClamAV signature validation gate (
clamscan -d) before deployment;sigup()SIGUSR2 reload only on validation success. Closes #467. monitor_scan_owner_filterstoggle. Default0restores v1.6.6 monitor semantics so root-owned malware drops are scanned regardless ofscan_ignore_root. Closes #485.- Two-file
ignore_inotifymodel: LMD-managedignore_inotify.defaults(systemd-private tmpdirs, modern MariaDB, PostgreSQL, Redis, ClamAV runtime) plus user-ownedignore_inotify;literal:per-entry escape prefix for paths with regex metacharacters. Closes #480, #484. - Logrotate config (
/etc/logrotate.d/maldet, weekly, 12 rotations) when logrotate is available. - Symlink-farm enforcement at startup (
pkg_fhs_verify_farm), FHS fallback sourcing (/usr/lib/maldet) when legacy symlinks are broken, and portable source-tree execution somaldetruns directly fromgit clonewith noinstall.sh(LMD_BASEDIRenv override). - Audit log with 7 event types (purge, update, alert_failed, hookscan source/rate metadata).
- Importconf extracted from
install.shinto a standalone helper for shared use. - Package install tests verifying FHS layout, symlink farm, and explicit permissions modes.
Changes & polish
- Vendored libs synced to canonical: tlog_lib 2.0.6, alert_lib 1.0.7, elog_lib 1.0.6, pkg_lib 1.0.10.
- Alert templates: summary consolidated into headers,
TOTALprefix dropped, quarantine metrics added, aligned column spacing. - Hook scans write to a rolling
hook.hits.loginstead of creating per-scan session files;genalert()suppressed for hook scans. - Scan engine: HEX+CSIG merged into a single worker pass; scan stage reorder (strlen runs last); bulk awk HEX classifier;
gensigs()awk compiler. _scan_progress(): plain newline output in non-TTY contexts (no ANSI), logarithmic backoff on non-native engines, watchdog semantics for engine health.- FHS log path:
/var/log/maldet/is authoritative for all install methods;$inspath/logsis a backward-compatible symlink;install.shmigrates existing logs preserving timestamps. --maintenance: session compression age 1h → 30d, archive age 30d → 90d,scan_meta_cleanup_age24h → 48h; newmaint_compress_ageandmaint_archive_age(0 disables).cron.daily: runs--maintenanceaftersigup/versionup; hardcoded log paths replaced with$maldet_log.scan_workersdefault0→auto(legacy0still accepted).scan_hexdepthdefault 512 KB → 256 KB; covers 98.9% of HEX patterns with YARA backstop.- Update verification prefers SHA-256 over MD5; graceful MD5 fallback when
sha256sumis absent. maldet.service:EnvironmentFilemoved to/etc/sysconfig/maldetor/etc/default/maldet; init script uses-b.-E/--dump-reportsuppressed from help; case handler retained for backward compat.inotify_verboseandtelegram_file_captionconfig variables removed (dead code).compat.confnow maps deprecated variable names;import_custsigs_*_url→sig_import_*_url.- Lifecycle JSON:
scanid→scan_id(alias retained for one cycle, removed in v2.1.0);workersnormalized to int. Closes #482.
Bug fixes (themed)
Monitor mode
- ERE semantics restored for
ignore_inotifyentries;literal:escape prefix added;ignore_pathsusesgrep -E -vfto match scan-mode semantics. Closes #484, #104, #431. - Ownership filters no longer drop root-owned malware silently; gated behind
monitor_scan_owner_filters(default off). Closes #485. ignore_inotifyunion-loaded from defaults + user overrides. Closes #480.- Filtered-cycle event-count logging added when all events drop on tier-2 scan filter.
JSON / reporting
--json-report list: path field parity, unified JSON escaping, dedup+escape rewrite eliminates O(N²) hang at ~20K sessions (82s → 1.7s); active[] gains lifecycle schema. Closes #482.reports[]globally sorted bystarted_epochnewest-first across TSV index + legacy passes; pass-2 glob skips legacysession.*.htmlartifacts (12s+ hangs on upgrades). Closes #483._json_escape_string: bash parameter expansion replaces sed pipeline (FreeBSD + multi-newline fix).view_report(): on-demand text rendering from TSV;newest/empty SCANID resolve to most recent session. Closes #336.
Scan lifecycle
- Background scans:
BASHPIDis recorded instead of parent shell$$;scan.metastoresns_pidseparately for correct signal delivery. - Scan progress: TTY line cleared before stage transitions to prevent garbled output.
-L/--list-active: live elapsed for running scans (was 0h 00m).- Checkpoint resume:
evalremoved;printf -v+-coallowlist rejectsPATH,IFS,LD_PRELOADfrom tampered checkpoint files. --pausesuppresses console progress and elapsed timer; elapsed excludes paused duration.- CSIG compiler rejects invalid
&&separator and universal subsigs in OR groups. scan()exits 1 when all provided paths are missing.- Hash scanning handles
md5sum/sha256sumbackslash-escaped filenames.
Quarantine & cleanup
- Correct scan ID in report warning, TOCTOU
mvexit-code check, inode naming replaces$RANDOM, batch signature name, colon truncation,-qbatch summary now writes to stdout. Closes #399. purge()dotfile glob:find -deletecatches dotfiles intmp/quarantine/sess.- Restore: orphan
.inforemoved after success;chmodquotes$file_mode. - Temp-file leaks:
clean.$$,suspend.users.$$,.alert_html.*,.tmpf_get.*cleaned up.
ClamAV
- Sig deployment perms 644, empty sig cleanup, linksigs ordering, version-gated
.hsb, MD5 merge format corrected for custom user signatures,_count_signatures()reads on-disk files. Closes #476. clamselector()logs warning onclamdtest failure instead of silent switch; no longer overwritesscan_max_filesize. Closes #410, #452.clamav_linksigsmktemp guard prevents writing sig files into filesystem root when staging fails.rfxn.hdbleading-backslash bug fixed (md5sum filenames containing backslash). Closes #468.
Alerting
- Slack
files.upload→files.getUploadURLExternal+completeUploadExternal; Telegram/botprefix; token security viacurl -K;genalert()state isolation. Closes #387, #458, #461, #426. - HTML email rendered on-demand at send time; smtp-relay guards
html_filebeforecaton text-format emails. - Slack alerts now fire from monitor mode. Closes #387.
--test-alert digestcursor pre-seed prevents real-hit leakage into test email and re-send.
Security
_safe_source_conf()allowlist (79 vars),conf.maldet0640, dirs 750.- Remote config RCE prevention; semver validation prevents directory traversal; hookscan validates ModSecurity filenames;
--uservalidates against path traversal. - JSON injection prevention; email header injection prevention.
- 19 bare
exitstatements given explicit codes;-c,-x,-i,-q,-n,-s,--web-proxyreject missing/empty arguments. Closes #366. - Hook validation: world-writable detection bug (broken glob → bash substring) fixed.
Install / packaging
- DEB
override_dh_fixpermsrestores 640/750 modes (72 files includingconf.maldet.hookscan.default,/etc/default/maldet, sub-library shells) after debhelper normalizes to 755/644. RPM uses%attr()and is unaffected. - RPM/DEB run signature update on fresh install (was missing); migrate core sig files from tarball installs; link ClamAV sigs and signal
clamdreload; sharedpkg-postinst.shfor install.sh post-install parity. - RPM/DEB
%pre: defensive detection for dir-vs-symlink cpio conflict preventsrename failed - Is a directoryon upgrades from priorinstall.shor partial installs. - Package manifests:
lmd_lifecycle.shadded to RPM spec, DEB rules, symlink-manifest. install.sh: detects supervisor and legacy monitor; graceful stop+restart on upgrade; man page compression on fresh install; backup robustness; no longer kills inotify monitoring; Slackware initrc.NAMEconvention; preserves hookscan config andpub/state. Closes #414.- Tar fallback excludes
pkg/directory (waspkg/buildonly).
FreeBSD & portability
- Detection via
uname -s;id -u(POSIX);restore()touch -tmtime; portablecommand cp/mv/rmthroughout;scan_strlenguard (wc -Lis GNU-only). mktempreplaces$RANDOM/$$;command -vreplaceswhich;grep -Ereplacesegrep; POSIX:chown separator; hardcoded binary paths replaced. Closes #71, #72, #270, #177.
Find/ignore filters
ignore_file_ext,--exclude-regex,--include-regex,ignore_pathsconverted to bash arrays for correct argument passing;scan_ignore_user/grouphandles non-existent entries. Closes #72, #335, #438, #440, #446, #450.
Misc
tlogcursor unit mismatch inpurge()andmonitor_cycle()fixed;eddependency replaced. Closes #227, #308.cron.dailyflock lock leak to backgrounded scans; lockfile prevents overlapping runs; config errors propagate to cron. Closes #373.sigignore()moved to runtime copies ingensigs(); signatures no longer irrecoverably lost between updates.sigup: writes downloaded version tomaldet.sigs.verto prevent CDN desync repeated downloads.scanengine isolates worker temp files using parent PID namespace to prevent concurrent scan conflicts.- Quarantine restore quotes
$file_modeagainst corrupt.infofiles. - Alert rendering: hit-type regex handles
{MD5}/{CSIG}/{SHA256}prefixes; emptyquarpathfield shift; emptySUMMARY_*tokens in single-hit messaging. --reportrecognizeslatestas alias fornewest.-e liststrips timezone offset from TSV session timestamps for consistent alignment.restore()/restore_hitlist()return exit 1 on all error paths.- YARA rules no longer counted as active when no engine is available.
Issues closed in this release
#28 #71 #72 #104 #198 #227 #239 #265 #267 #268 #277 #308 #335 #336 #366 #373 #381 #387 #392 #399 #410 #414 #426 #431 #438 #440 #446 #447 #450 #451 #452 #454 #458 #459 #461 #466 #467 #468 #476 #477 #480 #482 #483 #484 #485
Coverage
- Anvil distro matrix on the rc4 HEAD: 9 OS targets, 1080 BATS tests each, 0 failures.
- UAT: 100/100 on Debian 12.
- CI smoke-test workflow GREEN on master.
Backward compatibility
The CLI is frozen. Every existing argument continues to behave as it did in v1.6.6.1. New options are additive only. compat.conf maps every renamed configuration variable, and old configs auto-migrate on load.
Credits
Thanks to @Gazoo for the detailed reproductions on #482, #483, #484, #485, and to everyone who reported the long-standing issues that made it into this release.
The tarball below is the canonical install artifact (git archive honoring .gitattributes; excludes tests/, pkg/, docs/, working files).