This is an incomplete list of awful things the popular library zlib does that it shouldn't do. While upstream zlib is probably perfectly ideal for those using obscure or non-standard platforms, it's completely unacceptable for inclusion in modern operating systems. Please fix zlib and remove everything I complain about, or I'll do it myself when I get around to it.
If you haven't already, please read the source code of the latest upstream zlib release. Ask yourself whether you trust this code to be maintainable, robust, and suitable for being linking into whatever security conscious programs you use.
The basic idea was that some useless debuggers for microprocessors could not handle static symbols, so all uses of the static keyword was replaced by a `local' macro defined as:
#if !defined(local) #define local static#endif
Where the user could -Dlocal to remove the static keyword from functions and variables all over the source tree (to satisfy their broken debugger). Naturally, this was inconsistently applied in the source tree where some files didn't use the local macro. One file even used the #define local for the opposite purpose where it wanted the static keyword on micro controllers to avoid having big buffers on the stack.
For extra fun, a number of files unconditionally did:
#define local static
Which absolutely defeated the purpose and then proceeded use the local macro exclusively instead of static.
The code is filled with K&R function prototypes and K&R function declarations:
The OF macro is used to control whether real function prototypes are used, just in case the compiler is actually compiled with a compiler that doesn't implement the 1989 C standard.
The code is filled with clever typedefs for standard built-in types that should have been used instead. These types certainly shouldn't have been provided by the public headers. For example:
#if !defined(__MACTYPES__)typedef unsigned char Byte;#endiftypedef unsigned int uInt;typedef unsigned long uLong;typedef unsigned char uch;typedef uch FAR uchf;typedef unsigned short ush;typedef ush FAR ushf;typedef unsigned long ulg;
The code still uses far pointers to be compatible with 16-bit segmented systems like MS-DOS. You'll find lots of obscure helper types like Bytef* and uLongf* instead of unsigned char* and unsigned long*.
What's more concerning is the complete lack of useful standard typedefs in the public API and in the core library. Instead of size_t, the code is often using uInt or uLong. It occasionally needs fixed-size integers but doesn't use the standard <stdint.h>. Instead it tries to locate them at compile-time and embeds this information in generated headers.
This is just the surface: Check the <zconf.h> and <zlib.h> headers for the real unspeakable horror.
The library somehow found it fitting to have its own NULL declaration rather than the one from <stddef.h>
#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
Unspeakable horror and namespace pollution.
It's sarcastically totally useful to be able to #define Z_PREFIX to cause a bunch of macros to occur that renames everything to what it should have been called in the first place.
TODO: Investigate what's up with how zlib allocates memory.
TODO: What is this? Is it unspeakable too?
TODO: What's up with this?
TODO: What's up with this?
Because it's sometimes useful to be able to make particular const keywords go away, but only inconsistently and not all of them.
Because the system off_t isn't good enough. Okay, it might not be due to large file support silliness. There's also z_off64_t.
The <zconf.h> header likes to provide SEEK_SET if the standard library didn't.
#if !defined(SEEK_SET) && !defined(Z_SOLO)# define SEEK_SET 0 /* Seek from beginning of file. */# define SEEK_CUR 1 /* Seek from current position. */# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */#endif
Unsafe insane initialization on first use. Pretends it's thread safe when in doubt, but not really.
gzprintf truncation on long strings
That's right. It just uses whatever buffer room is available and truncates if it's too long. Or buffer overflows on insecure platforms.
This is an incomplete list. Look at the source code and you'll find much more.
Stuff to look into:
<zutil.h> has a whole bunch of WTF as well.
A custom bad memcpy for platforms without it.
Uses of the register keyword.
#define EQUAL 0 /* result of memcmp for equal strings */
Path duplication in gc_open.
What's up with ZLIB_INTERNAL doubling as a macro detecting whether this is inside zlib?
Nasty checks whether an unsigned int fits in an int in gzread.
Hilariously bad replacement routines for memcpy, memcmp, memset in zutil.c.
Multiplication overflow prone zcalloc. Is this function meant to clear memory?