• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding:utf-8 -*-
3#
4# Copyright (c) 2021 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18from __future__ import print_function
19import argparse
20import os
21import re
22import sys
23import subprocess
24import zipfile
25import errno
26import pipes
27import traceback
28import time
29import copy
30import shutil
31from pprint import pprint
32import stat
33
34from tools.colored import Colored
35from tools.templates import GN_ENTRY_TEMPLATE
36from tools.templates import PROJECT_GN_TEMPLATE
37from tools.templates import PROJECT_DEMO_TEMPLATE
38from tools.templates import PROJECT_HEADER_TEMPLATE
39from tools.templates import PROJECT_XML_TEMPLATE
40from tools.run_result import RunResult
41
42FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
43MODES = stat.S_IWUSR | stat.S_IRUSR
44
45CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
46SOURCE_ROOT_DIR = os.path.dirname(
47    os.path.dirname(
48        os.path.dirname(os.path.dirname(CURRENT_DIR))
49        )
50    )
51SOURCE_OUT_DIR = os.path.join(SOURCE_ROOT_DIR, "out")
52
53TDD_BUILD_GN_PATH = os.path.join(
54    SOURCE_ROOT_DIR,
55    "test/testfwk/developer_test/BUILD.gn"
56    )
57###project name must end with _fuzzer. eg. my_fuzzer,extrator_fuzzer.
58VALID_PROJECT_NAME_REGEX = re.compile(r'^[a-zA-Z0-9_-]+(_fuzzer)+$')
59
60
61def _add_environment_args(parser):
62    """Add common environment args."""
63    parser.add_argument(
64        '-t',
65        '--target_platform',
66        default='phone',
67        choices=["phone", "ivi", "plato"],
68        help="set target_platform value, default:phone")
69
70    parser.add_argument(
71        '-f',
72        '--filter',
73        default=None,
74        help="subsystem filter")
75
76
77def _get_command_string(command):
78    """Returns a shell escaped command string."""
79    return ' '.join(pipes.quote(part) for part in command)
80
81
82def parse_projects_path():
83    path_list = []
84    with open(TDD_BUILD_GN_PATH, 'r') as gn_file:
85        for line in gn_file.readlines()[4:]:
86            striped_str = line.strip()
87            if striped_str.endswith("]"):
88                break
89            path_list.append(striped_str.split(":")[0][3:])
90    return path_list
91
92
93def _get_fuzzer_yaml_config(fuzzer_name):
94    project_yaml_path = os.path.join(
95        CURRENT_DIR,
96        "projects",
97        fuzzer_name,
98        "project.yaml")
99    if not os.path.exists(project_yaml_path):
100        return {}
101    #log run stdout to fuzzlog dir
102    with open(project_yaml_path) as filehandle:
103        yaml_config = yaml.safe_load(filehandle)
104    return yaml_config
105
106
107#generate template fuzzer project
108def generate(args):
109    print("project name %s." % args.project_name)
110    print("project path %s." % args.project_path)
111    color_logger = Colored.get_project_logger()
112
113    if not VALID_PROJECT_NAME_REGEX.match(args.project_name):
114        print('Invalid project name.', file=sys.stderr)
115        return 1
116
117    template_args = {
118        'project_name': args.project_name,
119        'author': "",
120        'email': ""
121    }
122
123    project_dir_path = os.path.join(args.project_path, args.project_name)
124    print("project_dir_path %s." % project_dir_path)
125    try:
126        os.mkdir(project_dir_path)
127    except OSError as os_exception:
128        if os_exception.errno != errno.EEXIST:
129            raise
130        print(color_logger.red('%s already exists.' % project_dir_path),
131            file=sys.stderr)
132        return 1
133    color_logger.green('Writing new files to %s' % project_dir_path)
134
135    file_path = os.path.join(project_dir_path, 'project.xml')
136    if os.path.exists(file_path):
137        os.remove(file_path)
138    with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle:
139        filehandle.write(PROJECT_XML_TEMPLATE % template_args)
140
141    file_path = os.path.join(project_dir_path, "%s.cpp" % args.project_name)
142    if os.path.exists(file_path):
143        os.remove(file_path)
144    with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle:
145        filehandle.write(PROJECT_DEMO_TEMPLATE % template_args)
146
147    file_path = os.path.join(project_dir_path, "%s.h" % args.project_name)
148    if os.path.exists(file_path):
149        os.remove(file_path)
150    with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle:
151        filehandle.write(PROJECT_HEADER_TEMPLATE % template_args)
152    file_path = os.path.join(project_dir_path, "BUILD.gn")
153    if os.path.exists(file_path):
154        os.remove(file_path)
155    with os.fdopen(os.open(file_path, FLAGS, MODES), 'w') as filehandle:
156        filehandle.write(PROJECT_GN_TEMPLATE % template_args)
157
158    corpus_dir = os.path.join(project_dir_path, 'corpus')
159    if not os.path.exists(corpus_dir):
160        os.mkdir(corpus_dir)
161        if os.path.exists(os.path.join(corpus_dir, 'init')):
162            os.remove(os.path.join(corpus_dir, 'init'))
163        with os.fdopen(os.open(os.path.join(corpus_dir, 'init'), FLAGS, MODES), 'w') as filehandle:
164            filehandle.write("FUZZ")
165    return 0
166
167
168#complie fuzzer project
169def make(args,  stdout=None):
170    """make fuzzer module."""
171    color_logger = Colored.get_project_logger()
172
173    pre_cmd = ['cd', SOURCE_ROOT_DIR]
174    build_target_platform = "build_platform=\"%s\""
175
176    build_script = [
177        './build.sh',
178        '--gn-args',
179        'build_example=true',
180        '--build-target'
181    ]
182    build_script.append(args.project_name)
183    build_script.append("--gn-args")
184    build_script.append(build_target_platform % args.build_platform)
185    build_script.append("--product-name")
186    build_script.append(args.build_platform)
187    build_script.append("--export-para")
188    build_script.append("PYCACHE_ENABLE:true")
189    build_script.append("--export-para")
190    build_script.append("BUILD_AOSP:false")
191    print("BUILD_SCRIPT %s" % build_script)
192    final_cmd = "%s && %s" % (
193        _get_command_string(pre_cmd),
194        _get_command_string(build_script)
195        )
196
197    color_logger.green('Running:%s' % final_cmd)
198
199    subsystem_src_flag_file_path = os.path.join(
200        SOURCE_OUT_DIR,
201        "release/current_build_fuzz_target.txt"
202    )
203    if not os.path.exists(os.path.dirname(subsystem_src_flag_file_path)):
204        os.makedirs(os.path.dirname(subsystem_src_flag_file_path))
205    if os.path.exists(subsystem_src_flag_file_path):
206        os.remove(subsystem_src_flag_file_path)
207    with os.fdopen(os.open(subsystem_src_flag_file_path, FLAGS, MODES), 'wb') as file_handle:
208        file_handle.write(args.project_name.encode())
209
210    try:
211        if stdout:
212            ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR,
213                                        stdout=stdout)
214        else:
215            ret = subprocess.check_call(build_script, cwd=SOURCE_ROOT_DIR)
216        return ret
217    except subprocess.CalledProcessError:
218        print("*" * 50)
219        print("*" * 50)
220        print(
221            'fuzzers {} build failed.'.format(args.project_name),
222            file=sys.stdout
223        )
224        return -1
225
226
227def report(args):
228    pass
229
230
231def coverage_all(args):
232    pass
233
234
235def main():
236    parser = argparse.ArgumentParser(
237        'fuzzer_helper.py',
238        description='hydra-fuzz helpers'
239    )
240    subparsers = parser.add_subparsers(dest='command')
241
242    generate_parser = subparsers.add_parser(
243        'generate',
244        help='Generate files for new project.name must end with "_fuzzer".')
245    generate_parser.add_argument('project_name')
246    generate_parser.add_argument('project_path')
247    _add_environment_args(generate_parser)
248
249    make_parser = subparsers.add_parser(
250        'make', help='Build a single fuzzer module project. ')
251    make_parser.add_argument('project_name')
252    make_parser.add_argument('build_platform')
253    _add_environment_args(make_parser)
254
255    report_parser = subparsers.add_parser(
256       'report', help='Report fuzzer log'
257    )
258    _add_environment_args(report_parser)
259    report_parser.add_argument(
260        'subfunc',
261        default="list",
262        choices=["list", "coverage", "all"]
263    )
264    report_parser.add_argument(
265        "-i",
266        "--id",
267        required=False,
268        help="report ID, e.g. empty_fuzzer.20200211184850"
269    )
270
271    args = parser.parse_args()
272
273    if args.command == 'generate':
274        return generate(args)
275    elif args.command == 'make':
276        return make(args)
277
278    elif args.command == 'report':
279        report(args)
280        return 1
281    else:
282        return 0
283
284if __name__ == "__main__":
285    main()
286