From: "Ronald S. Bultje" <***@gmail.com>
Based on work (for GCI) by Aneesh Dogra <***@gmail.com>, and
inspired by patch in Chromium by Chris Evans <***@chromium.org>.
When turned on, H264/CAVLC gets ~15% (CVPCMNL1_SVA_C.264) slower for
ultra-high-bitrate files, or ~2.5% (CVFI1_SVA_C.264) for lower-bitrate
files. Other codecs are affected to a lesser extent because they are
less optimized; e.g., VC-1 slows down by less than 1% (all on x86).
The patch generated 3 extra instructions (cmp, cmovae and mov) per
call to get_bits().
---
configure | 2 ++
libavcodec/get_bits.h | 49 +++++++++++++++++++++++++++++++++++++++++++------
libavcodec/wmavoice.c | 2 ++
3 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/configure b/configure
index b3af2e9..056f409 100755
--- a/configure
+++ b/configure
@@ -113,6 +113,7 @@ Configuration options:
--disable-dxva2 disable DXVA2 code
--enable-runtime-cpudetect detect cpu capabilities at runtime (bigger binary)
--enable-hardcoded-tables use hardcoded tables instead of runtime generation
+ --disable-safe-bitstream-reader disable buffer boundary checking in bitreaders (faster, but may crash)
--enable-memalign-hack emulate memalign, interferes with memory debuggers
--disable-everything disable all components listed below
--disable-encoder=NAME disable encoder NAME
@@ -976,6 +977,7 @@ CONFIG_LIST="
rdft
rtpdec
runtime_cpudetect
+ safe_bitstream_reader
shared
sinewin
small
diff --git a/libavcodec/get_bits.h b/libavcodec/get_bits.h
index 7980284..e408e06 100644
--- a/libavcodec/get_bits.h
+++ b/libavcodec/get_bits.h
@@ -48,6 +48,23 @@
# endif
#endif
+/*
+ * Safe bitstream reading:
+ * optionally, the get_bits API can check to ensure that we
+ * don't read past input buffer boundaries. This is protected
+ * with CONFIG_SAFE_BITSTREAM_READER at the global level, and
+ * then below that with UNCHECKED_BITSTREAM_READER at the per-
+ * decoder level. This means that decoders that check internally
+ * can "#define UNCHECKED_BITSTREAM_READER 1" to disable
+ * overread checks.
+ * Boundary checking causes a minor performance penalty so for
+ * applications that won't want/need this, it can be disabled
+ * globally using "#define CONFIG_SAFE_BITSTREAM_READER 0".
+ */
+#ifndef UNCHECKED_BITSTREAM_READER
+#define UNCHECKED_BITSTREAM_READER !CONFIG_SAFE_BITSTREAM_READER
+#endif
+
/* bit input */
/* buffer, buffer_end and size_in_bits must be present and used by every reader */
typedef struct GetBitContext {
@@ -55,12 +72,12 @@ typedef struct GetBitContext {
#ifdef ALT_BITSTREAM_READER
int index;
#elif defined A32_BITSTREAM_READER
- uint32_t *buffer_ptr;
+ const uint32_t *buffer_ptr;
uint32_t cache0;
uint32_t cache1;
int bit_count;
#endif
- int size_in_bits;
+ int size_in_bits, size_in_bits_plus1;
} GetBitContext;
#define VLC_TYPE int16_t
@@ -144,7 +161,13 @@ for examples see get_bits, show_bits, skip_bits, get_vlc
# endif
// FIXME name?
+#if UNCHECKED_BITSTREAM_READER
# define SKIP_COUNTER(name, gb, num) name##_index += (num)
+#else
+# define SKIP_COUNTER(name, gb, num) name##_index = \
+ (gb)->size_in_bits_plus1 - name##_index < (num) ? \
+ (gb)->size_in_bits_plus1 : name##_index + (num)
+#endif
# define SKIP_BITS(name, gb, num) do { \
SKIP_CACHE(name, gb, num); \
@@ -171,7 +194,12 @@ static inline int get_bits_count(const GetBitContext *s){
}
static inline void skip_bits_long(GetBitContext *s, int n){
+#if UNCHECKED_BITSTREAM_READER
s->index += n;
+#else
+ s->index = s->size_in_bits_plus1 - s->index < n ?
+ s->size_in_bits_plus1 : s->index + n;
+#endif
}
#elif defined A32_BITSTREAM_READER
@@ -182,7 +210,7 @@ static inline void skip_bits_long(GetBitContext *s, int n){
int name##_bit_count = (gb)->bit_count; \
uint32_t name##_cache0 = (gb)->cache0; \
uint32_t name##_cache1 = (gb)->cache1; \
- uint32_t *name##_buffer_ptr = (gb)->buffer_ptr
+ const uint32_t *name##_buffer_ptr = (gb)->buffer_ptr
# define CLOSE_READER(name, gb) do { \
(gb)->bit_count = name##_bit_count; \
@@ -196,7 +224,9 @@ static inline void skip_bits_long(GetBitContext *s, int n){
const uint32_t next = av_be2ne32(*name##_buffer_ptr); \
name##_cache0 |= NEG_USR32(next, name##_bit_count); \
name##_cache1 |= next << name##_bit_count; \
- name##_buffer_ptr++; \
+ if (UNCHECKED_BITSTREAM_READER || \
+ name##_buffer_ptr < (const uint32_t *)(gb)->buffer_end) \
+ name##_buffer_ptr++; \
name##_bit_count -= 32; \
} \
} while (0)
@@ -224,13 +254,18 @@ static inline void skip_bits_long(GetBitContext *s, int n){
# define GET_CACHE(name, gb) name##_cache0
static inline int get_bits_count(const GetBitContext *s) {
- return ((uint8_t*)s->buffer_ptr - s->buffer)*8 - 32 + s->bit_count;
+ return ((const uint8_t*)s->buffer_ptr - s->buffer)*8 - 32 + s->bit_count;
}
static inline void skip_bits_long(GetBitContext *s, int n){
OPEN_READER(re, s);
re_bit_count += n;
+#if UNCHECKED_BITSTREAM_READER
re_buffer_ptr += re_bit_count>>5;
+#else
+ re_buffer_ptr = FFMIN(re_buffer_ptr + (re_bit_count >> 5),
+ (const uint32_t *) s->buffer_end);
+#endif
re_bit_count &= 31;
re_cache0 = av_be2ne32(re_buffer_ptr[-1]) << re_bit_count;
re_cache1 = 0;
@@ -311,7 +346,8 @@ static inline unsigned int get_bits1(GetBitContext *s){
result <<= index & 7;
result >>= 8 - 1;
#endif
- index++;
+ if (UNCHECKED_BITSTREAM_READER || s->index < s->size_in_bits_plus1)
+ index++;
s->index = index;
return result;
@@ -391,6 +427,7 @@ static inline void init_get_bits(GetBitContext *s,
s->buffer = buffer;
s->size_in_bits = bit_size;
+ s->size_in_bits_plus1 = bit_size + 1;
s->buffer_end = buffer + buffer_size;
#ifdef ALT_BITSTREAM_READER
s->index = 0;
diff --git a/libavcodec/wmavoice.c b/libavcodec/wmavoice.c
index 00e985d..8854e35 100644
--- a/libavcodec/wmavoice.c
+++ b/libavcodec/wmavoice.c
@@ -25,6 +25,8 @@
* @author Ronald S. Bultje <***@gmail.com>
*/
+#define UNCHECKED_BITSTREAM_READER 1
+
#include <math.h>
#include "avcodec.h"
#include "get_bits.h"
--
1.7.6