//************************************************************************ // compress.c // // Decompression code for MPPC compression. Compression code is on the // server, \nt\private\ntos\termsrv\rdp\rdpwd\compress.c. // // Copyright(c) Microsoft Corp., 1990-1999 // // Revision history: // 5/5/94 Created gurdeep // 4/20/1999 Optimized for TS jparsons, erikma // 8/24/2000 Fixed bugs nadima //************************************************************************ #include #include #include #include #ifdef COMPR_DEBUG #ifdef DECOMPRESS_KERNEL_DEBUG #include #include #include #include #include #include #include #include #include //Include all this stuff just to pickup icaapi.h #define DbgComprPrint(_x_) DbgPrint _x_ #define DbgComprBreakOnDbg IcaBreakOnDebugger #else //DECOMPRESS_KERNEL_DEBUG _inline ULONG DbgUserPrint(TCHAR* Format, ...) { va_list arglist; TCHAR Buffer[512]; ULONG retval; // // Format the output into a buffer and then print it. // va_start(arglist, Format); retval = _vsntprintf(Buffer, sizeof(Buffer)/sizeof(Buffer[0]), Format, arglist); if (retval != -1) { OutputDebugString(Buffer); OutputDebugString(_T("\n")); } return retval; } #define DbgComprPrint(_x_) DbgUserPrint _x_ #define DbgComprBreakOnDbg DebugBreak #endif //DECOMPRESS_KERNEL_DEBUG #endif //COMPR_DEBUG /* Bitptrs point to the current byte. The current bit (i.e. next bit to be * stored) is masked off by the bit entry. When this reaches zero, it is * reset to 0x80 and the next byte is set up. The bytes are filled MSBit * first. */ /* Starts the given bit pointer */ #define inbit_start(s) pbyte = s; bit = 16; byte=(*pbyte << 8) + *(pbyte+1); pbyte++; #define inbit_end() if (bit != 16) pbyte++; // // Without the ROBUSTness fix, the code will try to prefetch // a byte or more past then end of the input buffer which can // cause problems in kernel mode. // // We fixed this by forcing kernel mode to allocate the input // buffers with enough extra padding at the end so it's ok to // read past the end. If someday this needs to be changed // enable ROBUST_FIX, this _will_ slow down the algorithm. // // TODO: if we define ROBUST_FIX_ENABLE flag we have to handle a couple // of more cases that this code does not take into account. For instance // if you are at the end of the buffer and you do an in_bit_16 you will overread. // The in_bit_next/in_bit_advance macros for the robust fix should return // FALSE so the decompression fails in case we are at the end of the buffer // and we try to decompress further. As it is now the ROBUST_FIX macros will // not go past the end of the buffer but the in_bit_X (with X>7) will do. // #ifdef ROBUST_FIX_ENABLED #define in_bit_next() if (bit < 9) { \ bit=16; \ byte <<=8; \ ++pbyte; \ if(pbyte < inend) { \ byte |= *(pbyte); \ } \ } \ #define in_bit_advance() if (bit < 9) { \ bit+=8; \ byte <<=8; \ ++pbyte; \ if(pbyte < inend) { \ byte |= *(pbyte); \ } \ } #else //ROBUST_FIX_ENABLED #define in_bit_next() if (bit < 9) { \ bit=16; \ byte <<=8; \ byte |= *(++pbyte); \ } #define in_bit_advance() if (bit < 9) { \ bit+=8; \ byte <<=8; \ byte |= *(++pbyte); \ } #endif //ROBUST_FIX_ENABLED /* Returns non-zero in bitset if the next bit in the stream is a 1. */ #define in_bit() bit--; bitset = (byte >> bit) & 1; in_bit_next() #define in_bits_2(w) bit-=2; w = (byte >> bit) & 0x03;\ in_bit_advance(); #define in_bits_3(w) bit-=3; w = (byte >> bit) & 0x07;\ in_bit_advance(); #define in_bits_4(w) bit-=4; w = (byte >> bit) & 0x0F;\ in_bit_advance(); #define in_bits_5(w) bit-=5; w = (byte >> bit) & 0x1F;\ in_bit_advance(); #define in_bits_6(w) bit-=6; w = (byte >> bit) & 0x3F;\ in_bit_advance(); #define in_bits_7(w) bit-=7; w = (byte >> bit) & 0x7F;\ in_bit_advance(); #define in_bits_8(w) bit-=8; w = (byte >> bit) & 0xFF;\ bit+=8; byte <<=8; byte |= *(++pbyte); #define in_bits_9(w) bit-=9; w = (byte >> bit) & 0x1FF; \ bit+=8; byte <<=8; byte |= *(++pbyte); \ in_bit_advance(); #define in_bits_10(w) if (bit > 10) { \ bit-=10; w = (byte >> bit) & 0x3FF; \ bit+=8; byte <<=8; byte |= *(++pbyte); \ } else { \ in_bits_2(bitset); \ in_bits_8(w); \ w= w + (bitset << 8); \ } #define in_bits_11(w) if (bit > 11) { \ bit-=11; w = (byte >> bit) & 0x7FF; \ bit+=8; byte <<=8; byte |= *(++pbyte); \ } else { \ in_bits_3(bitset); \ in_bits_8(w); \ w= w + (bitset << 8); \ } #define in_bits_12(w) if (bit > 12) { \ bit-=12; w = (byte >> bit) & 0xFFF; \ bit+=8; byte <<=8; byte |= *(++pbyte); \ } else { \ in_bits_4(bitset); \ in_bits_8(w); \ w= w + (bitset << 8); \ } #define in_bits_13(w)\ if (bit > 13) { \ bit-=13; w = (byte >> bit) & 0x1FFF; \ bit+=8; byte <<=8; byte |= *(++pbyte); \ } else { \ in_bits_5(bitset); \ in_bits_8(w); \ w=w + (bitset << 8); \ } #define in_bits_14(w)\ if (bit > 14) { \ bit-=14; w = (byte >> bit) & 0x3FFF; \ bit+=8; byte <<=8; byte |= *(++pbyte); \ } else { \ in_bits_6(bitset); \ in_bits_8(w); \ w=w + (bitset << 8); \ } #define in_bits_15(w)\ if (bit > 15) { \ bit -= 15; w = (byte >> bit) & 0x7FFF; \ bit += 8; byte <<= 8; byte |= *(++pbyte); \ } else { \ in_bits_7(bitset); \ in_bits_8(w); \ w = w + (bitset << 8); \ } // initrecvcontext() // // Function: Initialize RecvContext block // // Parameters: IN context -> connection decompress context // // Callers of this API must allocate an object of the appropriate // type e.g RecvContext2_64K or RecvContext2_8K and set the // size field before casting to the generic type. // // The cleanest way to do this would be for initrecvcontext // to alloc the context itself but since the code is shared // between client and server there is no clean way to do this. // (callback allocators are _not_ clean). // // Returns status int initrecvcontext(RecvContext1 *context1, RecvContext2_Generic *context2, unsigned ComprType) { context1->CurrentPtr = context2->History; if(ComprType == PACKET_COMPR_TYPE_64K) { if(context2->cbSize > HISTORY_SIZE_64K) { context2->cbHistorySize = HISTORY_SIZE_64K-1; } else { return FALSE; } } else if(ComprType == PACKET_COMPR_TYPE_8K) { if(context2->cbSize > HISTORY_SIZE_8K) { context2->cbHistorySize = HISTORY_SIZE_8K-1; } else { return FALSE; } } else { return FALSE; } #if COMPR_DEBUG // // Setup debug fences to detect context buffer overwrites // if (context2->cbHistorySize == (HISTORY_SIZE_64K-1)) { ((RecvContext2_64K*)context2)->Debug16kFence = DEBUG_FENCE_16K_VALUE; } else if (context2->cbHistorySize == (HISTORY_SIZE_8K-1)) { ((RecvContext2_8K*)context2)->Debug8kFence = DEBUG_FENCE_8K_VALUE; } #endif memset(context2->History, 0, context2->cbHistorySize); return TRUE; } //* decompress() // // Function: de-compression function. // // Parameters: IN inbuf -> points to data to be uncompressed // IN inlen -> length of data // IN start -> flag indicating whether to start with a clean // history buffer // OUT output-> decompressed data // OUT outlen-> lenght of decompressed data // IN context -> connection decompress context // // Returns: TRUE if decompress was successful // FALSE if it wasnt // // WARNING: CODE IS HIGHLY OPTIMIZED FOR TIME. // int decompress( UCHAR FAR *inbuf, int inlen, int start, UCHAR FAR * FAR *output, int *outlen, RecvContext1 *context1, RecvContext2_Generic *context2, unsigned ComprType) { UCHAR FAR *inend; // When we know we're done decompressing UCHAR FAR *outstart; // Remember where in dbuf we started UCHAR FAR *current; long backptr = 0; // Back pointer for copy items long length; // Where to copy from in dbuf UCHAR FAR *s1, FAR *s2; int bitset; int bit; int byte; UCHAR FAR *pbyte; long HistorySize; // 2^N - 1 for fast modulo arithmetic below. const long HistorySizes[2] = { HISTORY_SIZE_8K - 1, HISTORY_SIZE_64K - 1 }; // // Important to validate the compression type otherwise we could overread // this HistroySizes array and then use a bogus size to validate further // down below // if (ComprType > PACKET_COMPR_TYPE_MAX) { return FALSE; } inend = inbuf + inlen; HistorySize = HistorySizes[ComprType]; // Start out looking at the first bit, which tells us whether to restart // the history buffer. inbit_start(inbuf); if (!start) current = context1->CurrentPtr; else context1->CurrentPtr = current = context2->History; // // Save our starting position // outstart = current; // // Decompress until we run out of input // while (pbyte < inend) { // // Jump on what to do with these three bits. // in_bits_3(length); switch (length) { case 0: in_bits_5(length); goto LITERAL; case 1: in_bits_5(length); length += 32; goto LITERAL; case 2: in_bits_5(length); length += 64; goto LITERAL; case 3: in_bits_5(length); length += 96; goto LITERAL; case 4: in_bits_6(length); length += 128; goto LITERAL; case 5: in_bits_6(length); length += 192; goto LITERAL; case 6: if (ComprType == PACKET_COMPR_TYPE_64K) { int foo; // 16-bit offset in_bits_8(foo); in_bits_8(backptr); backptr += (foo << 8) + 64 + 256 + 2048; } else { in_bits_13 (backptr); // 110 + 13 bit offset backptr += 320; } break; case 7: in_bit(); if (ComprType == PACKET_COMPR_TYPE_64K) { if (!bitset) { // 11-bit offset. in_bits_11(backptr); backptr += 64 + 256; } else { in_bit(); if (!bitset) { // 8-bit offset in_bits_8(backptr); backptr += 64; } else { in_bits_6(backptr) ; } } } else { if (!bitset) { in_bits_8(backptr); backptr+=64; } else { in_bits_6(backptr); } } break; } // // If we reach here, it's a copy item // in_bit() ; // 1st length bit if (!bitset) { length = 3; goto DONE; } in_bit(); // 2nd length bit if (!bitset) { in_bits_2 (length); length += 4; goto DONE; } in_bit(); // 3rd length bit if (!bitset) { in_bits_3 (length); length += 8; goto DONE; } in_bit(); // 4th length bit if (!bitset) { in_bits_4 (length); length += 16; goto DONE; } in_bit(); // 5th length bit if (!bitset) { in_bits_5 (length); length += 32; goto DONE; } in_bit(); // 6th length bit if (!bitset) { in_bits_6 (length); length += 64; goto DONE; } in_bit(); // 7th length bit if (!bitset) { in_bits_7 (length); length += 128; goto DONE; } in_bit(); // 8th length bit if (!bitset) { in_bits_8 (length); length += 256; goto DONE; } in_bit(); // 9th length bit if (!bitset) { in_bits_9 (length); length += 512; goto DONE; } in_bit(); // 10th length bit if (!bitset) { in_bits_10 (length); length += 1024; goto DONE; } in_bit(); // 11th length bit if (!bitset) { in_bits_11 (length); length += 2048; goto DONE; } in_bit(); // 12th length bit if (!bitset) { in_bits_12 (length); length += 4096; goto DONE; } in_bit(); // 13th length bit if (!bitset) { in_bits_13(length); length += 8192; goto DONE; } in_bit(); // 14th length bit if (!bitset) { in_bits_14(length); length += 16384; goto DONE; } in_bit(); // 15th length bit if (!bitset) { in_bits_15(length); length += 32768; goto DONE; } return FALSE; DONE: // Turn the backptr into an index location s2 = context2->History + (((current - context2->History) - backptr) & HistorySize); s1 = current; current += length; // If we are past the end of the history this is a bad sign: // abort decompression. Note this pointer comparison likely will not // work in Win16 since FAR pointers are not comparable. HUGE pointers // are too expensive to use here. // // We will also check the s2 pointer for overread. s2 will walk length // bytes and it could go outside the buffer. // We DON'T check current, s2 for underflow for the simle reason // that the length can't be more then 64k and the context2 buffer // can't be allocated in the last 64k of memory. if ((current < (context2->History + context2->cbHistorySize)) && ((s2+length) < context2->History + context2->cbHistorySize)) { // loop unrolled to handle length>backptr case *s1 = *s2; *(s1 + 1) = *(s2 + 1); s1 += 2; s2 += 2; length -= 2; // copy all the bytes while (length) { *s1++ = *s2++; length--; } // We have another copy item, and no literals continue; } else { #if COMPR_DEBUG DbgComprPrint(("Decompression Error - invalid stream or overrun atack!\n")); DbgComprPrint(("context1 %p, context2 %p, current 0x%8.8x, outstart 0x%8.8x\n", context1, context2, current, outstart)); DbgComprPrint(("inbuf 0x%8.8x, inlength %d, start 0x%8.8x\n", inbuf, inlen, start)); DbgComprPrint(("length 0x%x, s1 0x%x, s2 0x%x, bit 0x%x, byte 0x%x\n", length, s1, s2, bit, byte)); #endif return FALSE; } LITERAL: if (current < (context2->History + context2->cbHistorySize)) { // We have a literal *current++ = (UCHAR)length; } else { return FALSE; } } // while loop // End case: if ((bit == 16) && (pbyte == inend)) { if (current < (context2->History + context2->cbHistorySize)) { *current++ = *(pbyte - 1); } else { return FALSE; } } #if COMPR_DEBUG // // This code will ONLY test 64K decompression contexts // if ((context2->cbHistorySize == (HISTORY_SIZE_64K-1))) { if ((DEBUG_FENCE_16K_VALUE != ((RecvContext2_64K*)context2)->Debug16kFence)) { DbgComprPrint(("Decompression (16K) Error (mail tsstress) - (overwrote fence)!\n")); DbgComprPrint(("context1 %p, context2 %p, current 0x%8.8x, outstart 0x%8.8x\n", context1, context2, current, outstart)); DbgComprPrint(("inbuf 0x%8.8x, inlength %d, start 0x%8.8x\n", inbuf, inlen, start)); DbgComprPrint(("length 0x%x, s1 0x%x, s2 0x%x, bit 0x%x, byte 0x%x\n", length, s1, s2, bit, byte)); DbgComprBreakOnDbg(); } } else if ((context2->cbHistorySize == (HISTORY_SIZE_8K-1))) { if ((DEBUG_FENCE_8K_VALUE != ((RecvContext2_8K*)context2)->Debug8kFence)) { DbgComprPrint(("Decompression (8K) Error (mail tsstress) - (overwrote fence)!\n")); DbgComprPrint(("context1 %p, context2 %p, current 0x%8.8x, outstart 0x%8.8x\n", context1, context2, current, outstart)); DbgComprPrint(("inbuf 0x%8.8x, inlength %d, start 0x%8.8x\n", inbuf, inlen, start)); DbgComprPrint(("length 0x%x, s1 0x%x, s2 0x%x, bit 0x%x, byte 0x%x\n", length, s1, s2, bit, byte)); DbgComprBreakOnDbg(); } } #endif *outlen = (int)(current - outstart); // the length of decompressed data *output = context1->CurrentPtr; context1->CurrentPtr = current; return TRUE; }