rust-libteec 0.4.6

Rust implementation of TEE Client API for secure communication with Trusted Applications.
Documentation
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2025-2026 KylinSoft Co., Ltd. <https://www.kylinos.cn/>
// See LICENSES for license details.

#![allow(unused_variables)]

use std::ffi::CStr;

use libc::{c_char, c_int, size_t};

pub const TRACE_ERROR: c_int = 1;
pub const TRACE_INFO: c_int = 2;
pub const TRACE_DEBUG: c_int = 3;
pub const TRACE_FLOW: c_int = 4;

fn get_thread_id() -> i32 {
    unsafe { libc::syscall(libc::SYS_gettid) as i32 }
}

/// 判断字节是否为可打印ASCII字符(32-126)
#[allow(dead_code)]
fn is_printable_ascii(byte: u8) -> bool {
    (32..=126).contains(&byte)
}

/// 将日志级别常量转换为对应的字符串表示
fn get_level_string(level: c_int) -> &'static str {
    match level {
        TRACE_ERROR => "ERR",
        TRACE_INFO => "INF",
        TRACE_DEBUG => "DBG",
        TRACE_FLOW => "FLW",
        _ => "",
    }
}

// TODO: 实现 log_to_file 函数,当定义了 TEEC_LOG_FILE 时,
// 在函数最后调用 log_to_file 函数将信息写入日志中

/// 格式化输出日志信息到标准输出,格式为:
/// 级别 \[线程ID\] 前缀:函数名:行号: 消息
///
/// # 参数
/// - function: 函数名(可为null)
/// - line: 行号(<=0时忽略函数信息)
/// - level: 日志级别
/// - prefix: 模块前缀
/// - message: 日志消息
///
/// # 返回值
/// - 成功时返回输出的字节数,失败时返回-1
#[expect(clippy::not_unsafe_ptr_arg_deref)]
#[unsafe(no_mangle)]
pub extern "C" fn rust_dprintf(
    function: *const c_char,
    line: c_int,
    level: c_int,
    prefix: *const c_char,
    message: *const c_char,
) -> c_int {
    if prefix.is_null() || message.is_null() {
        return -1;
    }

    let msg = {
        // SAFETY: 已经检查 message 不为空
        let c_str = unsafe { CStr::from_ptr(message) };
        match c_str.to_str() {
            Ok(s) => s,
            Err(_) => return -1,
        }
    };

    let output = if !function.is_null() && line > 0 {
        // SAFETY: 已经检查 function 不为空
        // 使用 unwrap_or 提供有意义的默认值,避免静默失败
        let function_str = unsafe { CStr::from_ptr(function) }
            .to_str()
            .unwrap_or("<invalid_utf8>");

        // SAFETY: 已经检查 prefix 不为空
        let prefix_str = unsafe { CStr::from_ptr(prefix) }
            .to_str()
            .unwrap_or("<invalid_utf8>");
        let thread_id = get_thread_id();
        let level_str = get_level_string(level);

        format!("{level_str} [{thread_id}] {prefix_str}:{function_str}:{line}: {msg}\n")
    } else {
        format!("{msg}\n")
    };

    print!("{output}");

    output.len() as c_int
}

/// 十六进制dump函数,用于调试级别3和4
///
/// 以十六进制和 ASCII 形式输出内存缓冲区内容,格式类似 hexdump -C
/// 每行显示 16 字节,左侧为十六进制,右侧为可打印 ASCII 字符
///
/// # 参数
/// - bname: 缓冲区名称,显示在输出开头
/// - buffer: 待dump的内存缓冲区起始地址
/// - blen: 缓冲区长度
#[cfg(any(feature = "debug_level_3", feature = "debug_level_4"))]
#[expect(clippy::not_unsafe_ptr_arg_deref)]
#[unsafe(no_mangle)]
pub extern "C" fn dump_buffer(bname: *const c_char, buffer: *const u8, blen: size_t) {
    if bname.is_null() || buffer.is_null() || blen == 0 {
        return;
    }

    // SAFETY: 已经检查 bname 不为空
    // 使用 unwrap_or 提供有意义的默认值,便于识别编码问题
    let bname_str = unsafe { CStr::from_ptr(bname) }
        .to_str()
        .unwrap_or("<invalid_utf8>");

    eprintln!("#### {bname_str}");

    // SAFETY: 创建缓冲区切片,已经检查 buffer 不为空且 blen 指定了有效长度
    let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, blen) };
    let mut offset = 0;

    while offset < buffer_slice.len() {
        // 打印十六进制部分(每字节两位十六进制数)
        for i in 0..16 {
            if offset + i < buffer_slice.len() {
                eprint!("{:02x} ", buffer_slice[offset + i]);
            } else {
                eprint!("   "); // 填充空格保持对齐
            }

            if i == 7 {
                eprint!(" "); // 在8字节后添加额外空格
            }
        }

        eprint!(" |");

        // 打印 ASCII
        for i in 0..16 {
            if offset + i < buffer_slice.len() {
                let byte = buffer_slice[offset + i];
                if is_printable_ascii(byte) {
                    eprint!("{}", byte as char);
                } else {
                    eprint!("."); // 不可打印字符显示为.
                }
            }
        }

        eprintln!("|");

        offset += 16;
    }
}

/// dump_buffer 的空实现,用于未启用调试特性时
///
/// 保持相同的函数签名但函数体为空,避免链接错误
#[cfg(not(any(feature = "debug_level_3", feature = "debug_level_4")))]
#[unsafe(no_mangle)]
pub extern "C" fn dump_buffer(_bname: *const c_char, _buffer: *const u8, _blen: size_t) {
    // 空操作,仅在启用 debug_level_3 或 debug_level_4 时才有实际功能
}

#[cfg(test)]
mod teec_trace_tests {
    use super::*;
    use std::ffi::CString;

    fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
        let cstr = CString::new(s).expect("Failed to create CString");
        let ptr = cstr.as_ptr();
        (cstr, ptr)
    }

    #[test]
    fn test_is_printable_ascii() {
        // 测试各种边界情况
        assert!(is_printable_ascii(b'A'));
        assert!(is_printable_ascii(b'z'));
        assert!(is_printable_ascii(b' '));
        assert!(is_printable_ascii(b'~'));

        assert!(!is_printable_ascii(0));
        assert!(!is_printable_ascii(31));
        assert!(!is_printable_ascii(127));
        assert!(!is_printable_ascii(128));
        assert!(!is_printable_ascii(255));
    }

    #[test]
    fn test_get_level_string() {
        // 测试所有预定义的日志级别
        assert_eq!(get_level_string(TRACE_ERROR), "ERR");
        assert_eq!(get_level_string(TRACE_INFO), "INF");
        assert_eq!(get_level_string(TRACE_DEBUG), "DBG");
        assert_eq!(get_level_string(TRACE_FLOW), "FLW");
        assert_eq!(get_level_string(0), ""); // 未知级别
        assert_eq!(get_level_string(999), ""); // 无效级别
    }

    #[test]
    fn test_rust_dprintf_valid_input() {
        let function = Some("test_function");
        let line = 42;
        let level = TRACE_INFO;
        let prefix = "mylib";
        let message = "This is a test message";

        let (function_cstr, function_ptr) = if let Some(func) = function {
            make_c_string_with_ptr(func)
        } else {
            // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
            let cstr = CString::new("").expect("Empty string should always create CString");
            (cstr, std::ptr::null())
        };

        let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
        let (message_cstr, message_ptr) = make_c_string_with_ptr(message);

        let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);

        assert!(result > 0);
    }

    #[test]
    fn test_rust_dprintf_different_levels() {
        for (level, _expected_prefix) in [
            (TRACE_ERROR, "ERR"),
            (TRACE_INFO, "INF"),
            (TRACE_DEBUG, "DBG"),
            (TRACE_FLOW, "FLW"),
        ] {
            let function = Some("func");
            let line = 1;
            let prefix = "prefix";
            let message = "message";

            let (function_cstr, function_ptr) = if let Some(func) = function {
                make_c_string_with_ptr(func)
            } else {
                // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
                let cstr = CString::new("").expect("Empty string should always create CString");
                (cstr, std::ptr::null())
            };

            let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
            let (message_cstr, message_ptr) = make_c_string_with_ptr(message);

            let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);

            assert!(result > 0);
        }
    }

    #[test]
    fn test_rust_dprintf_no_function_info() {
        let level = TRACE_INFO;
        let prefix = "prefix";
        let message = "simple message";

        let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
        let (message_cstr, message_ptr) = make_c_string_with_ptr(message);

        let result = rust_dprintf(std::ptr::null(), 0, level, prefix_ptr, message_ptr);

        // 当 function 为 null 时,输出格式应该只有消息和换行
        // 返回值应该是消息长度加换行符
        assert_eq!(result, (message.len() + 1) as i32); // +1 为换行符
    }

    #[test]
    fn test_rust_dprintf_null_pointers() {
        // 测试空指针处理
        let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
        let result = rust_dprintf(
            std::ptr::null(),
            0,
            TRACE_INFO,
            prefix_ptr,
            std::ptr::null(),
        );
        assert_eq!(result, -1);

        let (message_cstr, message_ptr) = make_c_string_with_ptr("message");
        let result = rust_dprintf(
            std::ptr::null(),
            0,
            TRACE_INFO,
            std::ptr::null(),
            message_ptr,
        );
        assert_eq!(result, -1);

        let result = rust_dprintf(
            std::ptr::null(),
            0,
            TRACE_INFO,
            std::ptr::null(),
            std::ptr::null(),
        );
        assert_eq!(result, -1);
    }

    #[test]
    fn test_rust_dprintf_invalid_utf8() {
        // 测试无效 UTF-8 输入的处理
        let invalid_utf8 = b"valid\xFFpart\0";
        let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(invalid_utf8) };
        let ptr = cstr.as_ptr();

        let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
        let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, ptr);

        // 应该返回 -1,因为 UTF-8 转换失败
        assert_eq!(result, -1);
    }

    #[test]
    fn test_thread_id_positive() {
        // 线程 ID 应该是正数
        let thread_id = get_thread_id();
        assert!(thread_id > 0);
    }

    #[test]
    fn test_integration_style() {
        // 模拟实际使用场景
        let test_cases = vec![
            (
                Some("connect"),
                101,
                TRACE_ERROR,
                "network",
                "Connection failed",
            ),
            (
                Some("process"),
                205,
                TRACE_INFO,
                "worker",
                "Processing item",
            ),
            (
                Some("validate"),
                42,
                TRACE_DEBUG,
                "parser",
                "Validating input",
            ),
            (Some("loop"), 15, TRACE_FLOW, "main", "Iteration complete"),
        ];

        for (func, line, level, prefix, msg) in test_cases {
            let (function_cstr, function_ptr) = if let Some(f) = func {
                make_c_string_with_ptr(f)
            } else {
                // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
                let cstr = CString::new("").expect("Empty string should always create CString");
                (cstr, std::ptr::null())
            };

            let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
            let (message_cstr, message_ptr) = make_c_string_with_ptr(msg);

            let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);

            // 基本验证:函数应成功执行
            assert!(
                result > 0,
                "Failed for case: {:?}",
                (func, line, level, prefix, msg)
            );
        }
    }

    #[test]
    fn test_performance_no_panics() {
        // 确保多次调用不会 panic
        let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("perf");
        let (message_cstr, message_ptr) = make_c_string_with_ptr("test");

        for _ in 0..1000 {
            let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, message_ptr);
            assert!(result >= 0);
        }
    }

    // 测试 dump_buffer 的条件编译
    #[test]
    fn test_dump_buffer_exists() {
        // 验证dump_buffer函数存在且可调用
        let buffer = b"Hello, World!";
        let (name_cstr, name_ptr) = make_c_string_with_ptr("test_buffer");

        // 安全调用,只是验证接口存在
        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());

        // 测试空指针和零长度
        dump_buffer(std::ptr::null(), std::ptr::null(), 0);
        dump_buffer(name_ptr, std::ptr::null(), 10);
        dump_buffer(std::ptr::null(), buffer.as_ptr(), buffer.len());
    }

    #[test]
    fn test_dump_buffer_edge_cases() {
        // 测试 dump_buffer 的边界情况
        let buffer: [u8; 0] = [];
        let (name_cstr, name_ptr) = make_c_string_with_ptr("empty");

        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());

        let small_buffer = b"short";
        let (name2_cstr, name2_ptr) = make_c_string_with_ptr("small");

        dump_buffer(name2_ptr, small_buffer.as_ptr(), small_buffer.len());

        let non_printable = [0u8, 1, 2, 3, 128, 255, 10, 13];
        let (name3_cstr, name3_ptr) = make_c_string_with_ptr("non_printable");

        dump_buffer(name3_ptr, non_printable.as_ptr(), non_printable.len());
    }
}

#[cfg(all(test, any(feature = "debug_level_3", feature = "debug_level_4")))]
mod dump_buffer_tests {
    use super::*;
    use std::ffi::CString;

    // 测试辅助函数
    fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
        let cstr = CString::new(s).expect("Failed to create CString");
        let ptr = cstr.as_ptr();
        (cstr, ptr)
    }

    #[test]
    fn test_dump_buffer_output() {
        // 测试实际的dump_buffer输出格式(仅在启用相关特性时)
        let buffer = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%";
        let (name_cstr, name_ptr) = make_c_string_with_ptr("hex_dump_test");

        // 这个测试主要确保函数不 panic
        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
    }

    #[test]
    fn test_dump_buffer_exact_format() {
        // 测试精确的格式输出
        let buffer = vec![
            // 第一行:可打印ASCII
            b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
            b'E', b'F', // 第二行:混合内容
            0x00, 0x7F, 0x80, 0xFF, 32, 126, 10, 13, 255, 1, 2, 3, b'X', b'Y', b'Z', 0,
        ];

        let (name_cstr, name_ptr) = make_c_string_with_ptr("exact_format_test");

        // 这个测试主要确保函数不 panic
        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
    }
}