Commit 1a381404 authored by 4ast's avatar 4ast Committed by GitHub

Merge pull request #1840 from oriolarcas/feature/footer-header

Added new footer.h header where BPF_LICENSE is set if not defined
parents cb9773a9 639217fd
This diff is collapsed.
...@@ -53,6 +53,7 @@ This guide is incomplete. If something feels missing, check the bcc and kernel s ...@@ -53,6 +53,7 @@ This guide is incomplete. If something feels missing, check the bcc and kernel s
- [19. map.perf_read()](#19-mapperf_read) - [19. map.perf_read()](#19-mapperf_read)
- [20. map.call()](#20-mapcall) - [20. map.call()](#20-mapcall)
- [21. map.redirect_map()](#21-mapredirect_map) - [21. map.redirect_map()](#21-mapredirect_map)
- [Licensing](#licensing)
- [bcc Python](#bcc-python) - [bcc Python](#bcc-python)
- [Initialization](#initialization) - [Initialization](#initialization)
...@@ -87,6 +88,7 @@ This guide is incomplete. If something feels missing, check the bcc and kernel s ...@@ -87,6 +88,7 @@ This guide is incomplete. If something feels missing, check the bcc and kernel s
- [BPF Errors](#bpf-errors) - [BPF Errors](#bpf-errors)
- [1. Invalid mem access](#1-invalid-mem-access) - [1. Invalid mem access](#1-invalid-mem-access)
- [2. Cannot call GPL only function from proprietary program](#2-cannot-call-gpl-only-function-from-proprietary-program)
# BPF C # BPF C
...@@ -854,6 +856,28 @@ b.attach_xdp("eth1", out_fn, 0) ...@@ -854,6 +856,28 @@ b.attach_xdp("eth1", out_fn, 0)
Examples in situ: Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?l=C&q=redirect_map+path%3Aexamples&type=Code), [search /examples](https://github.com/iovisor/bcc/search?l=C&q=redirect_map+path%3Aexamples&type=Code),
## Licensing
Depending on which [BPF helpers](kernel-versions.md#helpers) are used, a GPL-compatible license is required.
The special BCC macro `BPF_LICENSE` specifies the license of the BPF program. You can set the license as a comment in your source code, but the kernel has a special interface to specify it programmatically. If you need to use GPL-only helpers, it is recommended to specify the macro in your C code so that the kernel can understand it:
```C
// SPDX-License-Identifier: GPL-2.0+
#define BPF_LICENSE GPL
```
Otherwise, the kernel may reject loading your program (see the [error description](#2-cannot-call-gpl-only-function-from-proprietary-program) below). Note that it supports multiple words and quotes are not necessary:
```C
// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
#define BPF_LICENSE Dual BSD/GPL
```
Check the [BPF helpers reference](kernel-versions.md#helpers) to see which helpers are GPL-only and what the kernel understands as GPL-compatible.
**If the macro is not specified, BCC will automatically define the license of the program as GPL.**
# bcc Python # bcc Python
## Initialization ## Initialization
...@@ -1521,3 +1545,16 @@ Traceback (most recent call last): ...@@ -1521,3 +1545,16 @@ Traceback (most recent call last):
raise Exception("Failed to load BPF program %s" % func_name) raise Exception("Failed to load BPF program %s" % func_name)
Exception: Failed to load BPF program kretprobe__inet_csk_accept Exception: Failed to load BPF program kretprobe__inet_csk_accept
``` ```
## 2. Cannot call GPL only function from proprietary program
This error happens when a GPL-only helper is called from a non-GPL BPF program. To fix this error, do not use GPL-only helpers from a proprietary BPF program, or relicense the BPF program under a GPL-compatible license. Check which [BPF helpers](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#helpers) are GPL-only, and what licenses are considered GPL-compatible.
Example calling `bpf_get_stackid()`, a GPL-only BPF helper, from a proprietary program (`#define BPF_LICENSE Proprietary`):
```
bpf: Failed to load program: Invalid argument
[...]
8: (85) call bpf_get_stackid#27
cannot call GPL only function from proprietary program
```
R"********(
/*
* Copyright (c) 2018 Clevernet, Inc.
*
* 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.
*/
#ifndef BPF_LICENSE
/* No license defined, using GPL
* You can define your own BPF_LICENSE in your C code */
#define BPF_LICENSE GPL
#endif
#define ___LICENSE(s) #s
#define __LICENSE(s) ___LICENSE(s)
#define _LICENSE __LICENSE(BPF_LICENSE)
char _license[] SEC("license") = _LICENSE;
)********"
...@@ -222,8 +222,6 @@ struct _name##_table_t _name = { .max_entries = (_max_entries) } ...@@ -222,8 +222,6 @@ struct _name##_table_t _name = { .max_entries = (_max_entries) }
#define cursor_advance(_cursor, _len) \ #define cursor_advance(_cursor, _len) \
({ void *_tmp = _cursor; _cursor += _len; _tmp; }) ({ void *_tmp = _cursor; _cursor += _len; _tmp; })
char _license[4] SEC("license") = "GPL";
unsigned _version SEC("version") = LINUX_VERSION_CODE; unsigned _version SEC("version") = LINUX_VERSION_CODE;
/* helper functions called from eBPF programs written in C */ /* helper functions called from eBPF programs written in C */
......
...@@ -43,4 +43,11 @@ map<string, const char *> ExportedFiles::headers_ = { ...@@ -43,4 +43,11 @@ map<string, const char *> ExportedFiles::headers_ = {
}, },
}; };
map<string, const char *> ExportedFiles::footers_ = {
{
"/virtual/include/bcc/footer.h",
#include "export/footer.h"
},
};
} }
...@@ -23,8 +23,10 @@ namespace ebpf { ...@@ -23,8 +23,10 @@ namespace ebpf {
class ExportedFiles { class ExportedFiles {
static std::map<std::string, const char *> headers_; static std::map<std::string, const char *> headers_;
static std::map<std::string, const char *> footers_;
public: public:
static const std::map<std::string, const char *> & headers() { return headers_; } static const std::map<std::string, const char *> & headers() { return headers_; }
static const std::map<std::string, const char *> & footers() { return footers_; }
}; };
} }
...@@ -1337,6 +1337,9 @@ void BFrontendAction::DoMiscWorkAround() { ...@@ -1337,6 +1337,9 @@ void BFrontendAction::DoMiscWorkAround() {
// CONFIG_CC_STACKPROTECTOR properly based on other configs, so it relieved any bpf // CONFIG_CC_STACKPROTECTOR properly based on other configs, so it relieved any bpf
// program (using task_struct, etc.) of patching the below code. // program (using task_struct, etc.) of patching the below code.
rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).InsertText(0, rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).InsertText(0,
"#if defined(BPF_LICENSE)\n"
"#error BPF_LICENSE cannot be specified through cflags\n"
"#endif\n"
"#if !defined(CONFIG_CC_STACKPROTECTOR)\n" "#if !defined(CONFIG_CC_STACKPROTECTOR)\n"
"#if defined(CONFIG_CC_STACKPROTECTOR_AUTO) \\\n" "#if defined(CONFIG_CC_STACKPROTECTOR_AUTO) \\\n"
" || defined(CONFIG_CC_STACKPROTECTOR_REGULAR) \\\n" " || defined(CONFIG_CC_STACKPROTECTOR_REGULAR) \\\n"
...@@ -1345,6 +1348,10 @@ void BFrontendAction::DoMiscWorkAround() { ...@@ -1345,6 +1348,10 @@ void BFrontendAction::DoMiscWorkAround() {
"#endif\n" "#endif\n"
"#endif\n", "#endif\n",
false); false);
rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).InsertTextAfter(
rewriter_->getSourceMgr().getBuffer(rewriter_->getSourceMgr().getMainFileID())->getBufferSize(),
"\n#include <bcc/footer.h>\n");
} }
void BFrontendAction::EndSourceFileAction() { void BFrontendAction::EndSourceFileAction() {
......
...@@ -70,7 +70,9 @@ ClangLoader::ClangLoader(llvm::LLVMContext *ctx, unsigned flags) ...@@ -70,7 +70,9 @@ ClangLoader::ClangLoader(llvm::LLVMContext *ctx, unsigned flags)
: ctx_(ctx), flags_(flags) : ctx_(ctx), flags_(flags)
{ {
for (auto f : ExportedFiles::headers()) for (auto f : ExportedFiles::headers())
remapped_files_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second); remapped_headers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second);
for (auto f : ExportedFiles::footers())
remapped_footers_[f.first] = llvm::MemoryBuffer::getMemBuffer(f.second);
} }
ClangLoader::~ClangLoader() {} ClangLoader::~ClangLoader() {}
...@@ -309,7 +311,9 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts, ...@@ -309,7 +311,9 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts,
return -1; return -1;
invocation0.getPreprocessorOpts().RetainRemappedFileBuffers = true; invocation0.getPreprocessorOpts().RetainRemappedFileBuffers = true;
for (const auto &f : remapped_files_) for (const auto &f : remapped_headers_)
invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
for (const auto &f : remapped_footers_)
invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); invocation0.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
if (in_memory) { if (in_memory) {
...@@ -341,7 +345,9 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts, ...@@ -341,7 +345,9 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts,
// give to it. Since the embedded header files should be copied fewer times // give to it. Since the embedded header files should be copied fewer times
// and reused if possible, set this flag to true. // and reused if possible, set this flag to true.
invocation1.getPreprocessorOpts().RetainRemappedFileBuffers = true; invocation1.getPreprocessorOpts().RetainRemappedFileBuffers = true;
for (const auto &f : remapped_files_) for (const auto &f : remapped_headers_)
invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
for (const auto &f : remapped_footers_)
invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); invocation1.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
invocation1.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf); invocation1.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf);
invocation1.getFrontendOpts().Inputs.clear(); invocation1.getFrontendOpts().Inputs.clear();
...@@ -367,7 +373,9 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts, ...@@ -367,7 +373,9 @@ int ClangLoader::do_compile(unique_ptr<llvm::Module> *mod, TableStorage &ts,
const_cast<const char **>(ccargs.data()) + ccargs.size(), diags)) const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
return -1; return -1;
invocation2.getPreprocessorOpts().RetainRemappedFileBuffers = true; invocation2.getPreprocessorOpts().RetainRemappedFileBuffers = true;
for (const auto &f : remapped_files_) for (const auto &f : remapped_headers_)
invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
for (const auto &f : remapped_footers_)
invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second); invocation2.getPreprocessorOpts().addRemappedFile(f.first, &*f.second);
invocation2.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf1); invocation2.getPreprocessorOpts().addRemappedFile(main_path, &*out_buf1);
invocation2.getFrontendOpts().Inputs.clear(); invocation2.getFrontendOpts().Inputs.clear();
......
...@@ -66,7 +66,8 @@ class ClangLoader { ...@@ -66,7 +66,8 @@ class ClangLoader {
std::string &mod_src, bool use_internal_bpfh); std::string &mod_src, bool use_internal_bpfh);
private: private:
std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> remapped_files_; std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> remapped_headers_;
std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> remapped_footers_;
llvm::LLVMContext *ctx_; llvm::LLVMContext *ctx_;
unsigned flags_; unsigned flags_;
}; };
......
...@@ -75,3 +75,5 @@ add_test(NAME py_test_usdt2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ...@@ -75,3 +75,5 @@ add_test(NAME py_test_usdt2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_test_usdt2 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt2.py) COMMAND ${TEST_WRAPPER} py_test_usdt2 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt2.py)
add_test(NAME py_test_usdt3 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} add_test(NAME py_test_usdt3 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_test_usdt3 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt3.py) COMMAND ${TEST_WRAPPER} py_test_usdt3 sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_usdt3.py)
add_test(NAME py_test_license WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} py_test_license sudo ${CMAKE_CURRENT_SOURCE_DIR}/test_license.py)
#!/usr/bin/env python
# Copyright (c) 2018 Clevernet, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
import unittest
from bcc import BPF
class TestLicense(unittest.TestCase):
gpl_only_text = """
#include <uapi/linux/ptrace.h>
struct gpl_s {
u64 ts;
};
BPF_PERF_OUTPUT(events);
int license_program(struct pt_regs *ctx) {
struct gpl_s data = {};
data.ts = bpf_ktime_get_ns();
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
"""
proprietary_text = """
#include <uapi/linux/ptrace.h>
struct key_t {
u64 ip;
u32 pid;
u32 uid;
char comm[16];
};
BPF_HASH(counts, struct key_t);
int license_program(struct pt_regs *ctx) {
struct key_t key = {};
u64 zero = 0 , *val;
u64 pid = bpf_get_current_pid_tgid();
u32 uid = bpf_get_current_uid_gid();
key.ip = PT_REGS_IP(ctx);
key.pid = pid & 0xFFFFFFFF;
key.uid = uid & 0xFFFFFFFF;
bpf_get_current_comm(&(key.comm), 16);
val = counts.lookup_or_init(&key, &zero); // update counter
(*val)++;
return 0;
}
"""
def license(self, lic):
return '''
#define BPF_LICENSE %s
''' % (lic)
def load_bpf_code(self, bpf_code):
event_name = bpf_code.get_syscall_fnname("read")
bpf_code.attach_kprobe(event=event_name, fn_name="license_program")
bpf_code.detach_kprobe(event=event_name)
def test_default(self):
b = BPF(text=self.gpl_only_text)
self.load_bpf_code(b)
def test_gpl_helper_macro(self):
b = BPF(text=self.gpl_only_text + self.license('GPL'))
self.load_bpf_code(b)
def test_proprietary_macro(self):
b = BPF(text=self.proprietary_text + self.license('Proprietary'))
self.load_bpf_code(b)
def test_gpl_compatible_macro(self):
b = BPF(text=self.gpl_only_text + self.license('Dual BSD/GPL'))
self.load_bpf_code(b)
def test_proprietary_words_macro(self):
b = BPF(text=self.proprietary_text + self.license('Proprietary license'))
self.load_bpf_code(b)
@unittest.expectedFailure
def test_cflags_fail(self):
b = BPF(text=self.gpl_only_text, cflags=["-DBPF_LICENSE=GPL"])
self.load_bpf_code(b)
@unittest.expectedFailure
def test_cflags_macro_fail(self):
b = BPF(text=self.gpl_only_text + self.license('GPL'), cflags=["-DBPF_LICENSE=GPL"])
self.load_bpf_code(b)
@unittest.expectedFailure
def test_empty_fail_macro(self):
b = BPF(text=self.gpl_only_text + self.license(''))
self.load_bpf_code(b)
@unittest.expectedFailure
def test_proprietary_fail_macro(self):
b = BPF(text=self.gpl_only_text + self.license('Proprietary license'))
self.load_bpf_code(b)
@unittest.expectedFailure
def test_proprietary_cflags_fail(self):
b = BPF(text=self.proprietary_text, cflags=["-DBPF_LICENSE=Proprietary"])
self.load_bpf_code(b)
if __name__ == "__main__":
unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment