##
## Maximum password lengths vary depending on kernel, hash type, and encoding
##

See https://hashcat.net/faq/lengths

##
## Generic hash modes only support salt lengths up to 256
##

This limitation on salt lengths only affects generic hash modes, such as md5(pass.salt).

Dedicated hash modes allow unlimited salt length support.

##
## Hashing algorithms that internally use UTF-16 characters could in special cases lead to false negatives
##

This applies to optimized kernels (-O) only:

The UTF-16 conversion implementation used within the kernel code is optimized for performance. For that
reason, hashcat does not respect all complicated encoding rules required to correctly convert, for instance,
ASCII or UTF-8 to UTF-16LE (or UTF-16BE). The implementation most likely fails with multi-byte characters,
because we basically add a zero byte every second byte within the kernel conversion code.

Since hashcat v6.2.1 there is true UTF-16 support for pure kernels. UTF-16 is fully supported.

There is an exception for mask attacks combined with compute devices that require vector data-types
to achieve full performance. For example a modern CPU. On those devices a mask attack over characters
outside ASCII needs --backend-vector-width 1. For modern GPUs it works as expected.

##
## The use of --keep-guessing can miss a colliding password
##

Hashcat reports cracked hashes through a buffer that holds one entry for every hash loaded, and that
buffer is emptied after each kernel invocation. Whichever candidate reaches a hash first takes its
entry. Without --keep-guessing that is all you need, because hashcat stops looking for a hash once it
is cracked.

With --keep-guessing the hash stays in the attack, so a second candidate can match it inside the same
kernel invocation. There is no entry left to put that one in, and it is dropped. The candidate that
survives is whichever the device reached first, so running the same two candidates in the other order
can report the other password.

Reporting every candidate of one kernel invocation would need an entry for every candidate that
invocation runs:

Number-of-MCU * Max-threads * Max-accel * Max-inner-loops * sizeof (plain_t)

For example, on an RTX 4090: 128 * 1024 * 1024 * 1024 * 40 = 5,497,558,138,880 bytes = 5120 GB VRAM

That settles it for a fast hash. It does not settle it for a slow one. A slow hash reports from its
_comp kernel once per work item, and its _loop kernel iterates the key derivation against the same
candidate rather than producing new ones, so the inner loop factor drops out and the bound is the
number of work items. On an RX 7900 XTX that is 17 MB of entries for mode 11600 and 396 MB for modes
23100, 24410 and 24420. No buffer of that shape is allocated today, so those modes lose collisions
the same way the fast ones do, but for them the memory to keep every collision is within reach.

##
## Hashcat GPU memory usage may be limited by maximum memory allocation sizes of OpenCL drivers
##

Most hashcat memory allocations are supposed to remain inside the same memory area.

The maximum size of a memory allocation is limited by GPU drivers / OpenCL runtimes.

Only a few modes (like scrypt) have special workarounds to make use of more than one allocation.

##
## The maximum number of functions per rule is limited to 31
##

This makes the size of one rule 128 byte.

On the other hand, there is a 25% OpenCL single allocation memory limit.

A typical GPU of today has 8GB = 2GB/128 = 16M rules max

If hashcat supported more functions per rule, it would be limited to fewer rules.

This is a trade-off game.

##
## Position identifiers in rules are limited to 36
##

The upper limit of maximum 36 positions for various rule functions (0-9, A-Z) was a design decision by the original authors of the rule engine.

##
## Known Issue: Unreleased memory after register spilling
##

When a kernel requires more registers than the hardware can provide,
the CUDA runtime automatically spills register data into global memory.
After the kernel finishes, this reserved memory is not released by the CUDA runtime.
The only way to free it is to destroy the CUDA context.

In normal runs this has no impact, since the context is destroyed at program exit.

In benchmarks the same context used to be kept across every hash-mode test, so available device
memory fell as the run went on, which cost benchmark performance and could end in a runtime error.
Hashcat now drops and recreates the context between modes, which gives the memory back. A device
whose context cannot be recreated is skipped rather than used through a handle that no longer
exists.

