To expand a bit: 16-byte chunks of memory can be associated with a four bit tag. Then you steal four unused high bits from your pointers to store a tag value. When memory has a tag, a pointer used to access it must have the matching tag in its high bits. Your malloc implementation can then assign a different tag to adjacent allocations and any overflow into an adjacent allocation will have a mismatched tag and will trap. Likewise, change the tag on free and an attempt to use the pointer after the allocation has been freed will trap.
Of course, this doesn't come for free. Four bits per 16 bytes means a 3% increase in memory needed for tagged memory, hardware overhead for checking tags on memory accesses, and software overhead of setting/changing/clearing tags as necessary. This overhead is low (Apple shipped this in flagship hardware a year ago and nobody's complaining about performance there) but not zero, and an implementation with poor performance could be a real problem.
4 bits per 16 bytes is ~ 1/4 the cost of ECC, which is possible to implement with just an extra cycle or two of latency in the memory controller. MTE seems similarly lightweight. Costs some transistors and a percent of a percent of power budget, but much like ECC it seems a fair bargain.