*     @xdp_md: pointer to xdp_md
  *     @delta: An positive/negative integer to be added to xdp_md.data
  *     Return: 0 on success or negative on error
+ *
+ * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
+ *     Copy a NUL terminated string from unsafe address. In case the string
+ *     length is smaller than size, the target is not padded with further NUL
+ *     bytes. In case the string length is larger than size, just count-1
+ *     bytes are copied and the last byte is set to NUL.
+ *     @dst: destination address
+ *     @size: maximum number of bytes to copy, including the trailing NUL
+ *     @unsafe_ptr: unsafe address
+ *     Return:
+ *       > 0 length of the string including the trailing NUL on success
+ *       < 0 error
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
        FN(set_hash_invalid),           \
        FN(get_numa_node_id),           \
        FN(skb_change_head),            \
-       FN(xdp_adjust_head),
+       FN(xdp_adjust_head),            \
+       FN(probe_read_str),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
 
        .arg2_type      = ARG_ANYTHING,
 };
 
+BPF_CALL_3(bpf_probe_read_str, void *, dst, u32, size,
+          const void *, unsafe_ptr)
+{
+       int ret;
+
+       /*
+        * The strncpy_from_unsafe() call will likely not fill the entire
+        * buffer, but that's okay in this circumstance as we're probing
+        * arbitrary memory anyway similar to bpf_probe_read() and might
+        * as well probe the stack. Thus, memory is explicitly cleared
+        * only in error case, so that improper users ignoring return
+        * code altogether don't copy garbage; otherwise length of string
+        * is returned that can be used for bpf_perf_event_output() et al.
+        */
+       ret = strncpy_from_unsafe(dst, unsafe_ptr, size);
+       if (unlikely(ret < 0))
+               memset(dst, 0, size);
+
+       return ret;
+}
+
+static const struct bpf_func_proto bpf_probe_read_str_proto = {
+       .func           = bpf_probe_read_str,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_UNINIT_MEM,
+       .arg2_type      = ARG_CONST_SIZE,
+       .arg3_type      = ARG_ANYTHING,
+};
+
 static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id)
 {
        switch (func_id) {
                return &bpf_current_task_under_cgroup_proto;
        case BPF_FUNC_get_prandom_u32:
                return &bpf_get_prandom_u32_proto;
+       case BPF_FUNC_probe_read_str:
+               return &bpf_probe_read_str_proto;
        default:
                return NULL;
        }