aboutsummaryrefslogtreecommitdiff
path: root/projects/opusfile/opusfile_fuzzer.c
blob: db162aad5ad5733c02f720e6b6e5a88a3c096579 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "opusfile/config.h"
#include "opusfile/include/opusfile.h"

// Opusfile fuzzing wrapper to help with automated fuzz testing. It's based on
// https://github.com/xiph/opusfile/blob/master/examples/opusfile_example.c
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  int ret, tmp;
  OggOpusFile *of = op_open_memory(data, size, &ret);
  if (!of)
    return 0;

  op_link_count(of);

  int link_index = -1;
  op_pcm_total(of, link_index);
  op_raw_total(of, link_index);
  op_pcm_tell(of);
  op_raw_tell(of);

  ogg_int64_t total_sample_count = 0;
  const int pcm_size = 120 * 48 * 2; // 120ms/ch@48kHz is recommended
  opus_int16 pcm[pcm_size];
  for (;;) {
    ret = op_read_stereo(of, pcm, pcm_size);
    if (ret < 0) {
      break;
    }

    if (op_current_link(of) != link_index) {
      link_index = op_current_link(of);
      op_pcm_total(of, link_index);
      op_raw_total(of, link_index);
      op_pcm_tell(of);
      op_raw_tell(of);
      op_bitrate_instant(of);
      tmp = op_head(of, link_index)->version;

      const OpusTags *tags = op_tags(of, link_index);
      for (int i = 0; i < tags->comments; ++i) {
        // Note: The compare also touches memory allocated for user_comments[i].
        // This is a desired side effect and should be kept even if this
        // comparison is removed.
        if (opus_tagncompare("METADATA_BLOCK_PICTURE", 22,
                             tags->user_comments[i]) == 0) {
          OpusPictureTag pic;
          if (opus_picture_tag_parse(&pic, tags->user_comments[i]) >= 0) {
            opus_picture_tag_clear(&pic);
          }
        }
      }

      if (tags->vendor) {
        tmp = tags->vendor[0];
      }

      int binary_suffix_len;
      opus_tags_get_binary_suffix(tags, &binary_suffix_len);
    }

    if (ret == 0) {
      break;
    }

    total_sample_count += ret;
  }

  if (total_sample_count > 0) {
    // Try random-access PCM reads. The number of tests is arbitrary and the
    // offset is designed to be pseudo-random, but deterministic - this is
    // implemented using Lehmer RNG with a minor hack that probably breaks some
    // properties of the RNG (but that is acceptable).
    ogg_int64_t rng_seed = 1307832949LL;
    for (int i = 0; i < 32; ++i) {
      // Derive the next deterministic offset to test and iterate the RNG.
      rng_seed = (rng_seed * 279470273LL);
      const ogg_int64_t offset = rng_seed % total_sample_count;
      rng_seed = rng_seed % 4294967291LL;

      if (op_pcm_seek(of, offset) == 0) {
        tmp = op_read_stereo(of, pcm, pcm_size);
      }
    }
  }

  op_free(of);
  return 0;
}