AWS Week in Review – October 10, 2022

Post Syndicated from Marcia Villalba original https://aws.amazon.com/blogs/aws/aws-week-in-review-october-10-2022/

This post is part of our Week in Review series. Check back each week for a quick roundup of interesting news and announcements from AWS!

I had an amazing start to the week last week as I was speaking at the AWS Community Day NL. This event had 500 attendees and over 70 speakers, and Dr. Werner Vogels, Amazon CTO, delivered the keynote. AWS Community Days are community-led conferences organized by local communities, with a variety of workshops and sessions. I recommend checking your region for any of these events.

Community Day NL

Last Week’s Launches
Here are some launches that got my attention during the previous week.

Amazon S3 Object Lambda now supports using your own code to change the results of HEAD and LIST requests, besides GET (which we launched last year). This feature now enables more capabilities for what you can do with S3 Object Lambda. Danilo made a Twitter thread with lots of use cases for this new launch.

Amazon SageMaker Clarify now can provide near real-time explanations for ML predictions. SageMaker Clarify is a service that provides explainability by ML models individual predictions. These explanations are important for developers to get visibility into their training data and models to identify potential bias.

AWS Storage Gateway now supports 15 TiB tapes. It increased the maximum supported virtual tape size on Tape Gateway from 5 TiB to 15 TiB, so you can store more data on a single virtual tape, and you can reduce the number of tapes you need to manage.

Amazon Aurora Serverless v2 now supports AWS CloudFormation. Early this year, we announced the general availability of Aurora Serverless v2, and now you can use AWS CloudFormation Templates to deploy and change the database along with the rest of your infrastructure.

AWS Config now supports 15 new resource types, including AWS DataSync, Amazon GuardDuty, Amazon Simple Email Service (Amazon SES), AWS AppSync, AWS Cloud Map, Amazon EC2, and AWS AppConfig. With this launch, you can use AWS Config to monitor configuration data for the supported resource types in your AWS account, and you can see how the configuration changes.

For a full list of AWS announcements, be sure to keep an eye on the What’s New at AWS page.

Other AWS News
Some other updates and news that you may have missed:

This week an article about how AWS is leading a pilot project to turn the Greek island of Naxos into a smart island caught my attention. The project introduces smart solutions for mobility, primary healthcare, and the transport of goods. The solution has been built based on four pillars that were important for the island: sustainability, telehealth, leisure, and digital skills. Check out the whole article to learn what they are doing.

Podcast Charlas Técnicas de AWS – If you understand Spanish, this podcast is for you. Podcast Charlas Técnicas is one of the official AWS podcasts in Spanish, and every other week there is a new episode. The podcast is meant for builders, and it shares stories about how customers implemented and learned AWS services, how to architect applications, and how to use new services. You can listen to all the episodes directly from your favorite podcast app or at AWS Podcasts en español.

AWS open-source news and updates – This is a newsletter curated by my colleague Ricardo to bring you the latest open-source projects, posts, events, and more.

Upcoming AWS Events
Check your calendars and sign up for these AWS events:

AWS re:Invent reserved seating opens on October 11. If you are planning to attend, book a spot in advance for your favorite sessions. AWS re:Invent is our biggest conference of the year, it happens in Las Vegas from November 28 to December 2, and registrations are open. Many writers of this blog have sessions at re:Invent, and you can search the event agenda using our names.

I started the post talking about AWS Community Days, and there is one in Warsaw, Poland, on October 14. If you are around Warsaw during this week, you can first check out the AWS Pop-up Hub in Warsaw that runs October 10-14 and then join for the Community Day.

On October 20, there is a virtual event for modernizing .NET workloads with Windows containers on AWS, You can register for free.

That’s all for this week. Check back next Monday for another Week in Review!

— Marcia

[$] A deeper look into the GCC Rust front-end

Post Syndicated from original https://lwn.net/Articles/909887/

Philip Herron and Arthur Cohen presented an
update
on the “gccrs” GCC front end for the Rust language at the
2022 Kangrejos conference. Less than
two weeks later — and joined by David Faust — they did it again at the 2022 GNU Tools Cauldron.
This time, though, they were talking to GCC developers and refocused their
presentation accordingly; the result was an interesting look into the
challenges of implementing a compiler for Rust.

Security updates for Monday

Post Syndicated from original https://lwn.net/Articles/910724/

Security updates have been issued by Debian (knot-resolver and libpgjava), Fedora (booth, dotnet3.1, expat, nheko, php-twig, php-twig2, php-twig3, poppler, python-joblib, and seamonkey), Mageia (colord, dbus, enlightenment, kitty, libvncserver, php, python3, and unbound), Slackware (libksba), SUSE (cyrus-sasl, ImageMagick, and xmlgraphics-commons), and Ubuntu (nginx and thunderbird).

Assembly within! BPF tail calls on x86 and ARM

Post Syndicated from Jakub Sitnicki original https://blog.cloudflare.com/assembly-within-bpf-tail-calls-on-x86-and-arm/

Assembly within! BPF tail calls on x86 and ARM

Assembly within! BPF tail calls on x86 and ARM

Early on when we learn to program, we get introduced to the concept of recursion. And that it is handy for computing, among other things, sequences defined in terms of recurrences. Such as the famous Fibonnaci numbersFn = Fn-1 + Fn-2.

Assembly within! BPF tail calls on x86 and ARM

Later on, perhaps when diving into multithreaded programming, we come to terms with the fact that the stack space for call frames is finite. And that there is an “okay” way and a “cool” way to calculate the Fibonacci numbers using recursion:

// fib_okay.c

#include <stdint.h>

uint64_t fib(uint64_t n)
{
        if (n == 0 || n == 1)
                return 1;

        return fib(n - 1) + fib(n - 2);
}

Listing 1. An okay Fibonacci number generator implementation

// fib_cool.c

#include <stdint.h>

static uint64_t fib_tail(uint64_t n, uint64_t a, uint64_t b)
{
    if (n == 0)
        return a;
    if (n == 1)
        return b;

    return fib_tail(n - 1, b, a + b);
}

uint64_t fib(uint64_t n)
{
    return fib_tail(n, 1, 1);
}

Listing 2. A better version of the same

If we take a look at the machine code the compiler produces, the “cool” variant translates to a nice and tight sequence of instructions:

⚠ DISCLAIMER: This blog post is assembly-heavy. We will be looking at assembly code for x86-64, arm64 and BPF architectures. If you need an introduction or a refresher, I can recommend “Low-Level Programming” by Igor Zhirkov for x86-64, and “Programming with 64-Bit ARM Assembly Language” by Stephen Smith for arm64. For BPF, see the Linux kernel documentation.

Assembly within! BPF tail calls on x86 and ARM

Listing 3. fib_cool.c compiled for x86-64 and arm64

The “okay” variant, disappointingly, leads to more instructions than a listing can fit. It is a spaghetti of basic blocks.

Assembly within! BPF tail calls on x86 and ARM

But more importantly, it is not free of x86 call instructions.

$ objdump -d fib_okay.o | grep call
 10c:   e8 00 00 00 00          call   111 <fib+0x111>
$ objdump -d fib_cool.o | grep call
$

This has an important consequence – as fib recursively calls itself, the stacks keep growing. We can observe it with a bit of help from the debugger.

$ gdb --quiet --batch --command=trace_rsp.gdb --args ./fib_okay 6
Breakpoint 1 at 0x401188: file fib_okay.c, line 3.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
n = 6, %rsp = 0xffffd920
n = 5, %rsp = 0xffffd900
n = 4, %rsp = 0xffffd8e0
n = 3, %rsp = 0xffffd8c0
n = 2, %rsp = 0xffffd8a0
n = 1, %rsp = 0xffffd880
n = 1, %rsp = 0xffffd8c0
n = 2, %rsp = 0xffffd8e0
n = 1, %rsp = 0xffffd8c0
n = 3, %rsp = 0xffffd900
n = 2, %rsp = 0xffffd8e0
n = 1, %rsp = 0xffffd8c0
n = 1, %rsp = 0xffffd900
13
[Inferior 1 (process 50904) exited normally]
$

While the “cool” variant makes no use of the stack.

$ gdb --quiet --batch --command=trace_rsp.gdb --args ./fib_cool 6
Breakpoint 1 at 0x40118a: file fib_cool.c, line 13.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
n = 6, %rsp = 0xffffd938
13
[Inferior 1 (process 50949) exited normally]
$

Where did the calls go?

The smart compiler turned the last function call in the body into a regular jump. Why was it allowed to do that?

It is the last instruction in the function body we are talking about. The caller stack frame is going to be destroyed right after we return anyway. So why keep it around when we can reuse it for the callee’s stack frame?

This optimization, known as tail call elimination, leaves us with no function calls in the “cool” variant of our fib implementation. There was only one call to eliminate – right at the end.

Once applied, the call becomes a jump (loop). If assembly is not your second language, decompiling the fib_cool.o object file with Ghidra helps see the transformation:

long fib(ulong param_1)

{
  long lVar1;
  long lVar2;
  long lVar3;
  
  if (param_1 < 2) {
    lVar3 = 1;
  }
  else {
    lVar3 = 1;
    lVar2 = 1;
    do {
      lVar1 = lVar3;
      param_1 = param_1 - 1;
      lVar3 = lVar2 + lVar1;
      lVar2 = lVar1;
    } while (param_1 != 1);
  }
  return lVar3;
}

Listing 4. fib_cool.o decompiled by Ghidra

This is very much desired. Not only is the generated machine code much shorter. It is also way faster due to lack of calls, which pop up on the profile for fib_okay.

But I am no performance ninja and this blog post is not about compiler optimizations. So why am I telling you about it?

Assembly within! BPF tail calls on x86 and ARM
Alex Dunkel (Maky), CC BY-SA 3.0, via Wikimedia Commons

Tail calls in BPF

The concept of tail call elimination made its way into the BPF world. Although not in the way you might expect. Yes, the LLVM compiler does get rid of the trailing function calls when building for -target bpf. The transformation happens at the intermediate representation level, so it is backend agnostic. This can save you some BPF-to-BPF function calls, which you can spot by looking for call -N instructions in the BPF assembly.

However, when we talk about tail calls in the BPF context, we usually have something else in mind. And that is a mechanism, built into the BPF JIT compiler, for chaining BPF programs.

We first adopted BPF tail calls when building our XDP-based packet processing pipeline. Thanks to it, we were able to divide the processing logic into several XDP programs. Each responsible for doing one thing.

Assembly within! BPF tail calls on x86 and ARM
Slide from “XDP based DDoS Mitigation” talk by Arthur Fabre

BPF tail calls have served us well since then. But they do have their caveats. Until recently it was impossible to have both BPF tails calls and BPF-to-BPF function calls in the same XDP program on arm64, which is one of the supported architectures for us.

Why? Before we get to that, we have to clarify what a BPF tail call actually does.

A tail call is a tail call is a tail call

BPF exposes the tail call mechanism through the bpf_tail_call helper, which we can invoke from our BPF code. We don’t directly point out which BPF program we would like to call. Instead, we pass it a BPF map (a container) capable of holding references to BPF programs (BPF_MAP_TYPE_PROG_ARRAY), and an index into the map.

long bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index)

       Description
              This  special  helper is used to trigger a "tail call", or
              in other words, to jump into  another  eBPF  program.  The
              same  stack frame is used (but values on stack and in reg‐
              isters for the caller are not accessible to  the  callee).
              This  mechanism  allows  for  program chaining, either for
              raising the maximum number of available eBPF instructions,
              or  to  execute  given programs in conditional blocks. For
              security reasons, there is an upper limit to the number of
              successive tail calls that can be performed.

bpf-helpers(7) man page

At first glance, this looks somewhat similar to the execve(2) syscall. It is easy to mistake it for a way to execute a new program from the current program context. To quote the excellent BPF and XDP Reference Guide from the Cilium project documentation:

Tail calls can be seen as a mechanism that allows one BPF program to call another, without returning to the old program. Such a call has minimal overhead as unlike function calls, it is implemented as a long jump, reusing the same stack frame.

But once we add BPF function calls into the mix, it becomes clear that the BPF tail call mechanism is indeed an implementation of tail call elimination, rather than a way to replace one program with another:

Tail calls, before the actual jump to the target program, will unwind only its current stack frame. As we can see in the example above, if a tail call occurs from within the sub-function, the function’s (func1) stack frame will be present on the stack when a program execution is at func2. Once the final function (func3) function terminates, all the previous stack frames will be unwinded and control will get back to the caller of BPF program caller.

Alas, one with sometimes slightly surprising semantics. Consider the code like below, where a BPF function calls the bpf_tail_call() helper:

struct {
    __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    __uint(max_entries, 1);
    __uint(key_size, sizeof(__u32));
    __uint(value_size, sizeof(__u32));
} bar SEC(".maps");

SEC("tc")
int serve_drink(struct __sk_buff *skb __unused)
{
    return 0xcafe;
}

static __noinline
int bring_order(struct __sk_buff *skb)
{
    bpf_tail_call(skb, &bar, 0);
    return 0xf00d;
}

SEC("tc")
int server1(struct __sk_buff *skb)
{
    return bring_order(skb);    
}

SEC("tc")
int server2(struct __sk_buff *skb)
{
    __attribute__((musttail)) return bring_order(skb);  
}

We have two seemingly not so different BPF programs – server1() and server2(). They both call the same BPF function bring_order(). The function tail calls into the serve_drink() program, if the bar[0] map entry points to it (let’s assume that).

Do both server1 and server2 return the same value? Turns out that – no, they don’t. We get a hex 🍔 from server1, and a ☕ from server2. How so?

First thing to notice is that a BPF tail call unwinds just the current function stack frame. Code past the bpf_tail_call() invocation in the function body never executes, providing the tail call is successful (the map entry was set, and the tail call limit has not been reached).

When the tail call finishes, control returns to the caller of the function which made the tail call. Applying this to our example, the control flow is serverX() --> bring_order() --> bpf_tail_call() --> serve_drink() -return-> serverX() for both programs.

The second thing to keep in mind is that the compiler does not know that the bpf_tail_call() helper changes the control flow. Hence, the unsuspecting compiler optimizes the code as if the execution would continue past the BPF tail call.

Assembly within! BPF tail calls on x86 and ARM
The call graph for server1() and server2() is the same, but the return value differs due to build time optimizations.

In our case, the compiler thinks it is okay to propagate the constant which bring_order() returns to server1(). Possibly catching us by surprise, if we didn’t check the generated BPF assembly.

We can prevent it by forcing the compiler to make a tail call to bring_order(). This way we ensure that whatever bring_order() returns will be used as the server2() program result.

🛈 General rule – for least surprising results, use musttail attribute when calling a function that contain a BPF tail call.

How does the bpf_tail_call() work underneath then? And why the BPF verifier wouldn’t let us mix the function calls with tail calls on arm64? Time to dig deeper.

Assembly within! BPF tail calls on x86 and ARM
Public Domain image

BPF tail call on x86-64

What does a bpf_tail_call() helper call translate to after BPF JIT for x86-64 has compiled it? How does the implementation guarantee that we don’t end up in a tail call loop forever?

To find out we will need to piece together a few things.

First, there is the BPF JIT compiler source code, which lives in arch/x86/net/bpf_jit_comp.c. Its code is annotated with helpful comments. We will focus our attention on the following call chain within the JIT:

do_jit() 🔗
  emit_prologue() 🔗
  push_callee_regs() 🔗
  for (i = 1; i <= insn_cnt; i++, insn++) {
    switch (insn->code) {
    case BPF_JMP | BPF_CALL:
      /* emit function call */ 🔗
    case BPF_JMP | BPF_TAIL_CALL:
      emit_bpf_tail_call_direct() 🔗
    case BPF_JMP | BPF_EXIT:
      /* emit epilogue */ 🔗
    }
  }

It is sometimes hard to visualize the generated instruction stream just from reading the compiler code. Hence, we will also want to inspect the input – BPF instructions – and the output – x86-64 instructions – of the JIT compiler.

To inspect BPF and x86-64 instructions of a loaded BPF program, we can use bpftool prog dump. However, first we must populate the BPF map used as the tail call jump table. Otherwise, we might not be able to see the tail call jump!

This is due to optimizations that use instruction patching when the index into the program array is known at load time.

# bpftool prog loadall ./tail_call_ex1.o /sys/fs/bpf pinmaps /sys/fs/bpf
# bpftool map update pinned /sys/fs/bpf/jmp_table key 0 0 0 0 value pinned /sys/fs/bpf/target_prog
# bpftool prog dump xlated pinned /sys/fs/bpf/entry_prog
int entry_prog(struct __sk_buff * skb):
; bpf_tail_call(skb, &jmp_table, 0);
   0: (18) r2 = map[id:24]
   2: (b7) r3 = 0
   3: (85) call bpf_tail_call#12
; return 0xf00d;
   4: (b7) r0 = 61453
   5: (95) exit
# bpftool prog dump jited pinned /sys/fs/bpf/entry_prog
int entry_prog(struct __sk_buff * skb):
bpf_prog_4f697d723aa87765_entry_prog:
; bpf_tail_call(skb, &jmp_table, 0);
   0:   nopl   0x0(%rax,%rax,1)
   5:   xor    %eax,%eax
   7:   push   %rbp
   8:   mov    %rsp,%rbp
   b:   push   %rax
   c:   movabs $0xffff888102764800,%rsi
  16:   xor    %edx,%edx
  18:   mov    -0x4(%rbp),%eax
  1e:   cmp    $0x21,%eax
  21:   jae    0x0000000000000037
  23:   add    $0x1,%eax
  26:   mov    %eax,-0x4(%rbp)
  2c:   nopl   0x0(%rax,%rax,1)
  31:   pop    %rax
  32:   jmp    0xffffffffffffffe3   // bug? 🤔
; return 0xf00d;
  37:   mov    $0xf00d,%eax
  3c:   leave
  3d:   ret

There is a caveat. The target addresses for tail call jumps in bpftool prog dump jited output will not make any sense. To discover the real jump targets, we have to peek into the kernel memory. That can be done with gdb after we find the address of our JIT’ed BPF programs in /proc/kallsyms:

# tail -2 /proc/kallsyms
ffffffffa0000720 t bpf_prog_f85b2547b00cbbe9_target_prog        [bpf]
ffffffffa0000748 t bpf_prog_4f697d723aa87765_entry_prog [bpf]
# gdb -q -c /proc/kcore -ex 'x/18i 0xffffffffa0000748' -ex 'quit'
[New process 1]
Core was generated by `earlyprintk=serial,ttyS0,115200 console=ttyS0 psmouse.proto=exps "virtme_stty_c'.
#0  0x0000000000000000 in ?? ()
   0xffffffffa0000748:  nopl   0x0(%rax,%rax,1)
   0xffffffffa000074d:  xor    %eax,%eax
   0xffffffffa000074f:  push   %rbp
   0xffffffffa0000750:  mov    %rsp,%rbp
   0xffffffffa0000753:  push   %rax
   0xffffffffa0000754:  movabs $0xffff888102764800,%rsi
   0xffffffffa000075e:  xor    %edx,%edx
   0xffffffffa0000760:  mov    -0x4(%rbp),%eax
   0xffffffffa0000766:  cmp    $0x21,%eax
   0xffffffffa0000769:  jae    0xffffffffa000077f
   0xffffffffa000076b:  add    $0x1,%eax
   0xffffffffa000076e:  mov    %eax,-0x4(%rbp)
   0xffffffffa0000774:  nopl   0x0(%rax,%rax,1)
   0xffffffffa0000779:  pop    %rax
   0xffffffffa000077a:  jmp    0xffffffffa000072b
   0xffffffffa000077f:  mov    $0xf00d,%eax
   0xffffffffa0000784:  leave
   0xffffffffa0000785:  ret
# gdb -q -c /proc/kcore -ex 'x/7i 0xffffffffa0000720' -ex 'quit'
[New process 1]
Core was generated by `earlyprintk=serial,ttyS0,115200 console=ttyS0 psmouse.proto=exps "virtme_stty_c'.
#0  0x0000000000000000 in ?? ()
   0xffffffffa0000720:  nopl   0x0(%rax,%rax,1)
   0xffffffffa0000725:  xchg   %ax,%ax
   0xffffffffa0000727:  push   %rbp
   0xffffffffa0000728:  mov    %rsp,%rbp
   0xffffffffa000072b:  mov    $0xcafe,%eax
   0xffffffffa0000730:  leave
   0xffffffffa0000731:  ret
#

Lastly, it will be handy to have a cheat sheet of mapping between BPF registers (r0, r1, …) to hardware registers (rax, rdi, …) that the JIT compiler uses.

BPF x86-64
r0 rax
r1 rdi
r2 rsi
r3 rdx
r4 rcx
r5 r8
r6 rbx
r7 r13
r8 r14
r9 r15
r10 rbp
internal r9-r12

Now we are prepared to work out what happens when we use a BPF tail call.

Assembly within! BPF tail calls on x86 and ARM

In essence, bpf_tail_call() emits a jump into another function, reusing the current stack frame. It is just like a regular optimized tail call, but with a twist.

Because of the BPF security guarantees – execution terminates, no stack overflows – there is a limit on the number of tail calls we can have (MAX_TAIL_CALL_CNT = 33).

Counting the tail calls across BPF programs is not something we can do at load-time. The jump table (BPF program array) contents can change after the program has been verified. Our only option is to keep track of tail calls at run-time. That is why the JIT’ed code for the bpf_tail_call() helper checks and updates the tail_call_cnt counter.

The updated count is then passed from one BPF program to another, and from one BPF function to another, as we will see, through the rax register (r0 in BPF).

Luckily for us, the x86-64 calling convention dictates that the rax register does not partake in passing function arguments, but rather holds the function return value. The JIT can repurpose it to pass an additional – hidden – argument.

The function body is, however, free to make use of the r0/rax register in any way it pleases. This explains why we want to save the tail_call_cnt passed via rax onto stack right after we jump to another program. bpf_tail_call() can later load the value from a known location on the stack.

This way, the code emitted for each bpf_tail_call() invocation, and the BPF function prologue work in tandem, keeping track of tail call count across BPF program boundaries.

But what if our BPF program is split up into several BPF functions, each with its own stack frame? What if these functions perform BPF tail calls? How is the tail call count tracked then?

Mixing BPF function calls with BPF tail calls

BPF has its own terminology when it comes to functions and calling them, which is influenced by the internal implementation. Function calls are referred to as BPF to BPF calls. Also, the main/entry function in your BPF code is called “the program”, while all other functions are known as “subprograms”.

Each call to subprogram allocates a stack frame for local state, which persists until the function returns. Naturally, BPF subprogram calls can be nested creating a call chain. Just like nested function calls in user-space.

BPF subprograms are also allowed to make BPF tail calls. This, effectively, is a mechanism for extending the call chain to another BPF program and its subprograms.

If we cannot track how long the call chain can be, and how much stack space each function uses, we put ourselves at risk of overflowing the stack. We cannot let this happen, so BPF enforces limitations on when and how many BPF tail calls can be done:

static int check_max_stack_depth(struct bpf_verifier_env *env)
{
        …
        /* protect against potential stack overflow that might happen when
         * bpf2bpf calls get combined with tailcalls. Limit the caller's stack
         * depth for such case down to 256 so that the worst case scenario
         * would result in 8k stack size (32 which is tailcall limit * 256 =
         * 8k).
         *
         * To get the idea what might happen, see an example:
         * func1 -> sub rsp, 128
         *  subfunc1 -> sub rsp, 256
         *  tailcall1 -> add rsp, 256
         *   func2 -> sub rsp, 192 (total stack size = 128 + 192 = 320)
         *   subfunc2 -> sub rsp, 64
         *   subfunc22 -> sub rsp, 128
         *   tailcall2 -> add rsp, 128
         *    func3 -> sub rsp, 32 (total stack size 128 + 192 + 64 + 32 = 416)
         *
         * tailcall will unwind the current stack frame but it will not get rid
         * of caller's stack as shown on the example above.
         */
        if (idx && subprog[idx].has_tail_call && depth >= 256) {
                verbose(env,
                        "tail_calls are not allowed when call stack of previous frames is %d bytes. Too large\n",
                        depth);
                return -EACCES;
        }
        …
}

While the stack depth can be calculated by the BPF verifier at load-time, we still need to keep count of tail call jumps at run-time. Even when subprograms are involved.

This means that we have to pass the tail call count from one BPF subprogram to another, just like we did when making a BPF tail call, so we yet again turn to value passing through the rax register.

Assembly within! BPF tail calls on x86 and ARM
Control flow in a BPF program with a function call followed by a tail call.

🛈 To keep things simple, BPF code in our examples does not allocate anything on stack. I encourage you to check how the JIT’ed code changes when you add some local variables. Just make sure the compiler does not optimize them out.

To make it work, we need to:

① load the tail call count saved on stack into rax before call’ing the subprogram,
② adjust the subprogram prologue, so that it does not reset the rax like the main program does,
③ save the passed tail call count on subprogram’s stack for the bpf_tail_call() helper to consume it.

A bpf_tail_call() within our suprogram will then:

④ load the tail call count from stack,
⑤ unwind the BPF stack, but keep the current subprogram’s stack frame in tact, and
⑥ jump to the target BPF program.

Now we have seen how all the pieces of the puzzle fit together to make BPF tail work on x86-64 safely. The only open question is does it work the same way on other platforms like arm64? Time to shift gears and dive into a completely different BPF JIT implementation.

Assembly within! BPF tail calls on x86 and ARM
Based on an image by Wutthichai Charoenburi, CC BY 2.0

Tail calls on arm64

If you try loading a BPF program that uses both BPF function calls (aka BPF to BPF calls) and BPF tail calls on an arm64 machine running the latest 5.15 LTS kernel, or even the latest 5.19 stable kernel, the BPF verifier will kindly ask you to reconsider your choice:

# uname -rm
5.19.12 aarch64
# bpftool prog loadall tail_call_ex2.o /sys/fs/bpf
libbpf: prog 'entry_prog': BPF program load failed: Invalid argument
libbpf: prog 'entry_prog': -- BEGIN PROG LOAD LOG --
0: R1=ctx(off=0,imm=0) R10=fp0
; __attribute__((musttail)) return sub_func(skb);
0: (85) call pc+1
caller:
 R10=fp0
callee:
 frame1: R1=ctx(off=0,imm=0) R10=fp0
; bpf_tail_call(skb, &jmp_table, 0);
2: (18) r2 = 0xffffff80c38c7200       ; frame1: R2_w=map_ptr(off=0,ks=4,vs=4,imm=0)
4: (b7) r3 = 0                        ; frame1: R3_w=P0
5: (85) call bpf_tail_call#12
tail_calls are not allowed in non-JITed programs with bpf-to-bpf calls
processed 4 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
…
#

That is a pity! We have been looking forward to reaping the benefits of code sharing with BPF to BPF calls in our lengthy machine generated BPF programs. So we asked – how hard could it be to make it work?

After all, BPF JIT for arm64 already can handle BPF tail calls and BPF to BPF calls, when used in isolation.

It is “just” a matter of understanding the existing JIT implementation, which lives in arch/arm64/net/bpf_jit_comp.c, and identifying the missing pieces.

To understand how BPF JIT for arm64 works, we will use the same method as before – look at its code together with sample input (BPF instructions) and output (arm64 instructions).

We don’t have to read the whole source code. It is enough to zero in on a few particular code paths:

bpf_int_jit_compile() 🔗
  build_prologue() 🔗
  build_body() 🔗
    for (i = 0; i < prog->len; i++) {
       build_insn() 🔗
         switch (code) {
         case BPF_JMP | BPF_CALL:
           /* emit function call */ 🔗
         case BPF_JMP | BPF_TAIL_CALL:
           emit_bpf_tail_call() 🔗
         }
    }
  build_epilogue() 🔗

One thing that the arm64 architecture, and RISC architectures in general, are known for is that it has a plethora of general purpose registers (x0-x30). This is a good thing. We have more registers to allocate to JIT internal state, like the tail call count. A cheat sheet of what roles the hardware registers play in the BPF JIT will be helpful:

BPF arm64
r0 x7
r1 x0
r2 x1
r3 x2
r4 x3
r5 x4
r6 x19
r7 x20
r8 x21
r9 x22
r10 x25
internal x9-x12, x26 (tail_call_cnt), x27

Now let’s try to understand the state of things by looking at the JIT’s input and output for two particular scenarios: (1) a BPF tail call, and (2) a BPF to BPF call.

It is hard to read assembly code selectively. We will have to go through all instructions one by one, and understand what each one is doing.

⚠ Brace yourself. Time to decipher a bit of ARM64 assembly. If this will be your first time reading ARM64 assembly, you might want to at least skim through this Guide to ARM64 / AArch64 Assembly on Linux before diving in.

Scenario #1: A single BPF tail call – tail_call_ex1.bpf.c

Input: BPF assembly (bpftool prog dump xlated)

   0: (18) r2 = map[id:4]           // jmp_table map
   2: (b7) r3 = 0
   3: (85) call bpf_tail_call#12
   4: (b7) r0 = 61453               // 0xf00d
   5: (95) exit

Output: ARM64 assembly (bpftool prog dump jited)

 0:   paciasp                            // Sign LR (ROP protection) ①
 4:   stp     x29, x30, [sp, #-16]!      // Save FP and LR registers ②
 8:   mov     x29, sp                    // Set up Frame Pointer
 c:   stp     x19, x20, [sp, #-16]!      // Save callee-saved registers ③
10:   stp     x21, x22, [sp, #-16]!      // ⋮ 
14:   stp     x25, x26, [sp, #-16]!      // ⋮ 
18:   stp     x27, x28, [sp, #-16]!      // ⋮ 
1c:   mov     x25, sp                    // Set up BPF stack base register (r10)
20:   mov     x26, #0x0                  // Initialize tail_call_cnt ④
24:   sub     x27, x25, #0x0             // Calculate FP bottom ⑤
28:   sub     sp, sp, #0x200             // Set up BPF program stack ⑥
2c:   mov     x1, #0xffffff80ffffffff    // r2 = map[id:4] ⑦
30:   movk    x1, #0xc38c, lsl #16       // ⋮ 
34:   movk    x1, #0x7200                // ⋮
38:   mov     x2, #0x0                   // r3 = 0
3c:   mov     w10, #0x24                 // = offsetof(struct bpf_array, map.max_entries) ⑧
40:   ldr     w10, [x1, x10]             // Load array->map.max_entries
44:   add     w2, w2, #0x0               // = index (0)
48:   cmp     w2, w10                    // if (index >= array->map.max_entries)
4c:   b.cs    0x0000000000000088         //     goto out;
50:   mov     w10, #0x21                 // = MAX_TAIL_CALL_CNT (33)
54:   cmp     x26, x10                   // if (tail_call_cnt >= MAX_TAIL_CALL_CNT)
58:   b.cs    0x0000000000000088         //     goto out;
5c:   add     x26, x26, #0x1             // tail_call_cnt++;
60:   mov     w10, #0x110                // = offsetof(struct bpf_array, ptrs)
64:   add     x10, x1, x10               // = &array->ptrs
68:   lsl     x11, x2, #3                // = index * sizeof(array->ptrs[0])
6c:   ldr     x11, [x10, x11]            // prog = array->ptrs[index];
70:   cbz     x11, 0x0000000000000088    // if (prog == NULL) goto out;
74:   mov     w10, #0x30                 // = offsetof(struct bpf_prog, bpf_func)
78:   ldr     x10, [x11, x10]            // Load prog->bpf_func
7c:   add     x10, x10, #0x24            // += PROLOGUE_OFFSET * AARCH64_INSN_SIZE (4)
80:   add     sp, sp, #0x200             // Unwind BPF stack
84:   br      x10                        // goto *(prog->bpf_func + prologue_offset)
88:   mov     x7, #0xf00d                // r0 = 0xf00d
8c:   add     sp, sp, #0x200             // Unwind BPF stack ⑨
90:   ldp     x27, x28, [sp], #16        // Restore used callee-saved registers
94:   ldp     x25, x26, [sp], #16        // ⋮
98:   ldp     x21, x22, [sp], #16        // ⋮
9c:   ldp     x19, x20, [sp], #16        // ⋮
a0:   ldp     x29, x30, [sp], #16        // ⋮
a4:   add     x0, x7, #0x0               // Set return value
a8:   autiasp                            // Authenticate LR
ac:   ret                                // Return to caller

① BPF program prologue starts with Pointer Authentication Code (PAC), which protects against Return Oriented Programming attacks. PAC instructions are emitted by JIT only if CONFIG_ARM64_PTR_AUTH_KERNEL is enabled.

Arm 64 Architecture Procedure Call Standard mandates that the Frame Pointer (register X29) and the Link Register (register X30), aka the return address, of the caller should be recorded onto the stack.

③ Registers X19 to X28, and X29 (FP) plus X30 (LR), are callee saved. ARM64 BPF JIT does not use registers X23 and X24 currently, so they are not saved.

④ We track the tail call depth in X26. No need to save it onto stack since we use a register dedicated just for this purpose.

⑤ FP bottom is an optimization that allows store/loads to BPF stack with a single instruction and an immediate offset value.

⑥ Reserve space for the BPF program stack. The stack layout is now as shown in a diagram in build_prologue() source code.

⑦ The BPF function body starts here.

bpf_tail_call() instructions start here.

⑨ The epilogue starts here.

Whew! That was a handful 😅.

Notice that the BPF tail call implementation on arm64 is not as optimized as on x86-64. There is no code patching to make direct jumps when the target program index is known at the JIT-compilation time. Instead, the target address is always loaded from the BPF program array.

Ready for the second scenario? I promise it will be shorter. Function prologue and epilogue instructions will look familiar, so we are going to keep annotations down to a minimum.

Scenario #2: A BPF to BPF call – sub_call_ex1.bpf.c

Input: BPF assembly (bpftool prog dump xlated)

int entry_prog(struct __sk_buff * skb):
   0: (85) call pc+1#bpf_prog_a84919ecd878b8f3_sub_func
   1: (95) exit
int sub_func(struct __sk_buff * skb):
   2: (b7) r0 = 61453                   // 0xf00d
   3: (95) exit

Output: ARM64 assembly

int entry_prog(struct __sk_buff * skb):
bpf_prog_163e74e7188910f2_entry_prog:
   0:   paciasp                                 // Begin prologue
   4:   stp     x29, x30, [sp, #-16]!           // ⋮
   8:   mov     x29, sp                         // ⋮
   c:   stp     x19, x20, [sp, #-16]!           // ⋮
  10:   stp     x21, x22, [sp, #-16]!           // ⋮
  14:   stp     x25, x26, [sp, #-16]!           // ⋮
  18:   stp     x27, x28, [sp, #-16]!           // ⋮
  1c:   mov     x25, sp                         // ⋮
  20:   mov     x26, #0x0                       // ⋮
  24:   sub     x27, x25, #0x0                  // ⋮
  28:   sub     sp, sp, #0x0                    // End prologue
  2c:   mov     x10, #0xffffffffffff5420        // Build sub_func()+0x0 address
  30:   movk    x10, #0x8ff, lsl #16            // ⋮
  34:   movk    x10, #0xffc0, lsl #32           // ⋮
  38:   blr     x10 ------------------.         // Call sub_func()+0x0 
  3c:   add     x7, x0, #0x0 <----------.       // r0 = sub_func()
  40:   mov     sp, sp                | |       // Begin epilogue
  44:   ldp     x27, x28, [sp], #16   | |       // ⋮
  48:   ldp     x25, x26, [sp], #16   | |       // ⋮
  4c:   ldp     x21, x22, [sp], #16   | |       // ⋮
  50:   ldp     x19, x20, [sp], #16   | |       // ⋮
  54:   ldp     x29, x30, [sp], #16   | |       // ⋮
  58:   add     x0, x7, #0x0          | |       // ⋮
  5c:   autiasp                       | |       // ⋮
  60:   ret                           | |       // End epilogue
                                      | |
int sub_func(struct __sk_buff * skb): | |
bpf_prog_a84919ecd878b8f3_sub_func:   | |
   0:   paciasp <---------------------' |       // Begin prologue
   4:   stp     x29, x30, [sp, #-16]!   |       // ⋮
   8:   mov     x29, sp                 |       // ⋮
   c:   stp     x19, x20, [sp, #-16]!   |       // ⋮
  10:   stp     x21, x22, [sp, #-16]!   |       // ⋮
  14:   stp     x25, x26, [sp, #-16]!   |       // ⋮
  18:   stp     x27, x28, [sp, #-16]!   |       // ⋮
  1c:   mov     x25, sp                 |       // ⋮
  20:   mov     x26, #0x0               |       // ⋮
  24:   sub     x27, x25, #0x0          |       // ⋮
  28:   sub     sp, sp, #0x0            |       // End prologue
  2c:   mov     x7, #0xf00d             |       // r0 = 0xf00d
  30:   mov     sp, sp                  |       // Begin epilogue
  34:   ldp     x27, x28, [sp], #16     |       // ⋮
  38:   ldp     x25, x26, [sp], #16     |       // ⋮
  3c:   ldp     x21, x22, [sp], #16     |       // ⋮
  40:   ldp     x19, x20, [sp], #16     |       // ⋮
  44:   ldp     x29, x30, [sp], #16     |       // ⋮
  48:   add     x0, x7, #0x0            |       // ⋮
  4c:   autiasp                         |       // ⋮
  50:   ret ----------------------------'       // End epilogue

We have now seen what a BPF tail call and a BPF function/subprogram call compiles down to. Can you already spot what would go wrong if mixing the two was allowed?

That’s right! Every time we enter a BPF subprogram, we reset the X26 register, which holds the tail call count, to zero (mov x26, #0x0). This is bad. It would let users create program chains longer than the MAX_TAIL_CALL_CNT limit.

How about we just skip this step when emitting the prologue for BPF subprograms?

@@ -246,6 +246,7 @@ static bool is_lsi_offset(int offset, int scale)
 static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
 {
        const struct bpf_prog *prog = ctx->prog;
+       const bool is_main_prog = prog->aux->func_idx == 0;
        const u8 r6 = bpf2a64[BPF_REG_6];
        const u8 r7 = bpf2a64[BPF_REG_7];
        const u8 r8 = bpf2a64[BPF_REG_8];
@@ -299,7 +300,7 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
        /* Set up BPF prog stack base register */
        emit(A64_MOV(1, fp, A64_SP), ctx);

-       if (!ebpf_from_cbpf) {
+       if (!ebpf_from_cbpf && is_main_prog) {
                /* Initialize tail_call_cnt */
                emit(A64_MOVZ(1, tcc, 0, 0), ctx);

Believe it or not. This is everything that was missing to get BPF tail calls working with function calls on arm64. The feature will be enabled in the upcoming Linux 6.0 release.

Outro

From recursion to tweaking the BPF JIT. How did we get here? Not important. It’s all about the journey.

Along the way we have unveiled a few secrets behind BPF tails calls, and hopefully quenched your thirst for low-level programming. At least for today.

All that is left is to sit back and watch the fruits of our work. With GDB hooked up to a VM, we can observe how a BPF program calls into a BPF function, and from there tail calls to another BPF program:

https://demo-gdb-step-thru-bpf.pages.dev/

Until next time 🖖.

Complex Impersonation Story

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2022/10/complex-impersonation-story.html

This is a story of one piece of what is probably a complex employment scam. Basically, real programmers are having their resumes copied and co-opted by scammers, who apply for jobs (or, I suppose, get recruited from various job sites), then hire other people with Western looks and language skills are to impersonate those first people on Zoom job interviews. Presumably, sometimes the scammers get hired and…I suppose…collect paychecks for a while until they get found out and fired. But that requires a bunch of banking fraud as well, so I don’t know.

EDITED TO ADD (10/11): Brian Krebs writes about fake LinkedIn profiles, which is probably another facet of this fraud system. Someone needs to unravel all of the threads.

Поглед от Украйна “Клинч в полза на проруските сили”. Украински вестник не вижда в изборите ни нищо добро за Киев

Post Syndicated from Николай Марченко original https://bivol.bg/%D0%BA%D0%BB%D0%B8%D0%BD%D1%87-%D0%B2-%D0%BF%D0%BE%D0%BB%D0%B7%D0%B0-%D0%BD%D0%B0-%D0%BF%D1%80%D0%BE%D1%80%D1%83%D1%81%D0%BA%D0%B8%D1%82%D0%B5-%D1%81%D0%B8%D0%BB%D0%B8-%D1%83%D0%BA.html

събота 8 октомври 2022


В България след дълга политическа криза се проведоха предсрочни избори за четвърти пореден път. Най-ниската избирателна активност за последните 32 години показа, че българите са уморени от избори. И има…

Friday Squid Blogging: Emotional Support Squid

Post Syndicated from Bruce Schneier original https://www.schneier.com/blog/archives/2022/10/friday-squid-blogging-emotional-support-squid.html

The Monterey Bay Aquarium has a video—”2 Hours Of Squid To Relax/Study/Work To“—with 2.4 million views.

As usual, you can also use this squid post to talk about the security stories in the news that I haven’t covered.

Read my blog posting guidelines here.

Седмицата (3–8 октомври)

Post Syndicated from Йовко Ламбрев original https://toest.bg/editorial-3-8-october-2022/

Изборите отминаха. И очаквано, не само не донесоха решение на кризата в управлението на страната, но и поставиха някои нови въпроси, чиито отговори съвсем не са очевидни. Но преди всичко…

През седмицата лидерът на „Възраждане“ Костадин Костадинов сътвори шумен скандал, опитвайки се да изгони от пресконференция неудобни нему журналисти. Това само по себе си не е новина. Помним и други политици, които обичаха да размахват пръст на медиите. Помним и такива, които, съзнавайки своята посредственост, градят кариера със слалом от един на друг скандал, за да поддържат вниманието към себе си. Но те успяват именно защото винаги има рояк от други журналисти, готови да припкат наоколо и безкритично да препредават тезите им. Вместо да ги удавят в тишина в името на професионалната чест и достойнството на цялото общество. Гръбнак е нужен и в политиката, но много повече в журналистиката.

Но да се върнем към изборите. Звучи екстравагантно, но до неотдавна всички участници в изборите все търсеха (че и намираха!) някакви доводи да твърдят, че именно те са спечелили. Интересно е, че след гласуването в неделя никой много-много не иска да се припознае като победител. Така че въпросът „Кой всъщност спечели изборите?“ изобщо не е неоснователен. В материала може да прочетете мнението на Светла Енчева по темата.

Емилия Милчева

След резултатите от гласуването в неделя коалициите изглеждат от трудни до невъзможни. Идеята ГЕРБ да пенсионира Борисов, за да се отвори някаква перспектива пред България, остава в сферата на невероятното. А прекрачването на установените червени линии от останалите ще е равносилно на политическо самоубийство. Затова и коментарът на Емилия Милчева тази седмица е с поглед малко по-напред във времето в опит да се предположи откъде би могла да дойде някаква развръзка. И уви, изглежда, че няма как да минем без някои „малки убийства“ след 2 октомври.

Мирослав Зафиров

В Европа кризите също нямат очевидни решения, а редица външни фактори и събития никак не улесняват нещата. „Трусовете в Източното Средиземноморие чертаят криза и за ЕС“, твърди в свой анализ за „Тоест“ нашият близкоизточен анализатор Мирослав Зафиров. От една страна, добрата новина е, че Израел и Ливан може би най-сетне са съвсем близо до споразумение за морската граница помежду си. От друга, темата е свързана със сериозни енергийни интереси и с надеждите на ЕС да задоволи част от енергийните си нужди именно с ресурсите от този регион.

Ангел Петров

Седмица преди България, предсрочни парламентарни избори се проведоха и в Италия. Част от партньорите в спечелилата коалиция не крият симпатиите си към Путинова Русия, а други, като медийния магнат Берлускони, са директни проводници на руските пропагандни послания – както лично, така и чрез свои медии. Ангел Петров проследява детайлите по темата в обзорен материал за съвместната ни рубрика с АЕЖ „Хроники на инфодемията“. Не пропускайте текста му „Червеят на съмнението. Повлия ли Русия на италианските избори?“

Севда Семер

Нека завършим с интересна книга. В рубриката ни „На второ четене“ този път Севда Семер е избрала да привлече читателския ни интерес към сборника с разкази „Американският любовник“ на Роуз Тримейн. Случайно или не, Севда завършва така своята препоръка:

Няма как да не се зачудиш какво щеше да е, ако всеки от нас осиновеше любимото си място за разходка и го поддържаше чисто. Сетих се за Смити и неговата отдаденост, когато излязоха резултатите от последната избирателна активност. В крайна сметка това сякаш е най-добрата препоръка за сборник с разкази – че героите остават да ти правят компания и след като си приключил с четенето.

Хубав уикенд и приятно четене!

Източник

След 2 октомври

Post Syndicated from Емилия Милчева original https://toest.bg/sled-2-oktomvri/

Политическата безизходица в България изглежда непреодолима. Не може да се получи устойчива коалиция без големи компромиси, а големите компромиси в контекста на скорошни нови избори са политическо самоубийство. ГЕРБ е отхвърлена от тези, с които е престижно да се коалира – като „Демократична България“ и „Продължаваме промяната“, които не я искат заради корупционния ѝ бранд и Бойко Борисов и свитата му. Желана е от други – като ДПС, официалното съюзяване с които би било репутационна щета.

ДБ и ПП поставят конкретни условия. Първите държат на членоразделна подкрепа за Украйна чрез официална оръжейна помощ (извън търговските сделки), реформа на прокуратурата и антиинфлационни мерки. Вторите – борба с корупцията по всички фронтове, което следва да се разбира и като оглавяване на КПКОНПИ от Бойко Рашков, избран за депутат от ПП.

Позицията за Украйна не фигурира в изброените от формацията на Кирил Петков и Асен Василев приоритети, някои от които са досущ като обявените преди време от БСП – данъчни облекчения за млади семейства с деца, две тарифи за ток и газ, увеличаване на минималната работна заплата на 860 лв. от 1 януари… и една съдебна реформа, за да не е толкова популистко. Но по-късно Петков отговори, че ще подкрепят в парламента изпращане на оръжие за Украйна – законопроект, който пък от ДБ обещаха да внесат още в първия ден.

Но ГЕРБ и Борисов без чадър? Трудно е за вярване.

Въпреки това Борисов трябва да е правдоподобен и убедителен в опитите си да конструира работещо правителство, след като съучаства в свалянето на коалиционния кабинет на ПП и проповядваше месеци наред, че само ГЕРБ ще спаси България от хаоса. Още тогава обеща оръжие за Украйна, таван на цените на бензина и дизела от 2,70 лв./л и евтино олио. (Последното така и така поевтинява поради обективни причини – складовете са претъпкани с българско и украинско слънчогледово семе.)

В легендата за Гордиевия възел Александър Македонски го разсякъл с меча си, но в политиката се налага възелът да бъде развързан. За тази цел ще се прибегне и до консултации на „Дондуков“ 2 – няма как, напоследък кабинетите на президента управляват повече от редовните правителства.

А възможно ли е четвъртият служебен кабинет на президента Румен Радев да работи заедно с 48-мия парламент?

За известно време – например два месеца, стига да не им е обидно на народните представители. Тогава политическите сили ще трябва да гласуват предложените от президентския кабинет политики в една парламентарна република, каквато е по конституция България. Да одобряват бюджет (на президента) за 2023 г. и законопроекти, свързани с Плана за възстановяване и устойчивост (ПВУ), подготвени пак от правителството на държавния глава. Някои от 20-те закона, изисквани заради реформите в ПВУ, вече бяха обявени от служебния кабинет – законопроектът за личния фалит; нов законопроект за КПКОНПИ, различен от внесения от ПП, в който Комисията се председателства на ротационен принцип от всеки от петимата ѝ членове, а те пък се избират от парламента, съдиите от ВАС и ВКС и президента.

А защо не и купуване на десет бойни изтребителя Gripen – компания, за която Радев лобира още като главнокомандващ ВВС. Сделката може да бъде пласирана в настоящото междувластие с аргумента, че американските F-16, които България плати, ще дойдат твърде късно – след 2030 г.

С изключение на БСП, никоя от останалите шест политически сили, спечелили представителство в 48-мия парламент, не е коментирала ходовете на служебния кабинет за самолетите на Gripen, разглеждани като „преходно решение“, камо ли да критикува президента. (Държавният глава се ползва със симпатиите на много избиратели от всички политически сили, дори и от средите на десните.) Такъв проект не фигурира в инвестиционната план-програма на Министерството на отбраната до 2032 г., но пък и този план не е гласуван в парламента, който би трябвало да одобри и сделка с шведския концерн. Изпълнителното бюро на БСП обаче се възпротиви с разумния аргумент, че не е работа на служебното правителство „да ангажира дългосрочно България с такива договори“.

Мекият вариант за полупрезидентска република може да се проиграе и с „припознаване“ на служебното правителство за редовно.

Такова предложение беше направила Мая Манолова след изборите през юли м.г. от името на „Изправи се! Мутри вън!“. В онзи момент победителят „Има такъв народ“ обяви, че няма да преговаря за коалиция, тъй като т.нар. протестни партии нямат 121 депутати – и ИТН ще предложи кабинет на малцинството със своите 64 народни представители. Сега обаче войната в Украйна наложи друг дневен ред и ако има „припознаване“, военният министър Димитър Стоянов може да изпадне заради твърде волатилната си позиция към руската агресия.

Президентът насърчи партиите да не се страхуват да поемат отговорност и да не проявяват малодушие с червените линии, които очертават пред съставянето на кабинет. Засега неговият бивш служебен премиер и бивш съветник Стефан Янев смело е декларирал готовността на „Български възход“ да състави кабинет на супермалцинството (евентуално с третия мандат). Което си е пак полупрезидентско управление. А ГЕРБ, след като са положили всички усилия, ще си измият ръцете…

На следващите предсрочни парламентарни избори – все едно кога, някъде там през февруари или март – 49-тият парламент ще е парцелиран като 48-мия, като 47-мия, защо не и като 46-тия. Отново никоя политическа сила няма да има комфорта да направи мнозинство с когото ѝ харесва, пак ще управлява някой президентски гълъб – и този път няколко „малки“ убийства са неизбежни.

Заглавна снимка: Ksenia Makagonova / Unsplash

Източник

Червеят на съмнението. Повлия ли Русия на италианските избори?

Post Syndicated from Ангел Петров original https://toest.bg/cherveyat-na-sumnenieto-povliya-li-rusiya-na-italianskite-izbori/

„Истината е първата жертва във всяка война.“ С тази крилата фраза през 2020 г. започнахме поредицата „Хроники на инфодемията“. Пандемията действително се оказа благодатна почва за разпространение на всякакъв вид дезинформация. Авторите на „Хрониките“ успяха да обяснят и да развенчаят едни от най-популярните митове, слухове и манипулативни наративи, свързани с COVID-19. След пандемията обаче започна истинска война в Европа. И истината отново е жертва. Пълномащабната агресия на Путинова Русия срещу Украйна започна след години прокремълска пропаганда, за която експертите предупреждаваха, но която често беше неглижирана. По тази причина от Асоциацията на европейските журналисти и Фондацията за свободата „Фридрих Науман“ в партньорство с „Тоест“ решихме да възобновим „Хрониките“ и да разширим тематичния им обхват – за ролята на дезинформацията като цяло, която в последните месеци създава и среда за войната.

Русия няма нужда от обичайните си инструменти за пропаганда (RT, „Спутник“ или „фабрики за тролове“), за да достигне до милиони европейци. През март т.г. Италия забрани руските канали RT и „Спутник“, но в началото на май частната телевизия Rete 4 покани руския външен министър Сергей Лавров на пространно интервю. На въпроса защо е необходима „денацификация“ на Украйна, след като самият президент Володимир Зеленски произлиза от еврейско семейство, Лавров отговори, че „и Хитлер е бил с еврейска кръв“ и „най-големите антисемити са именно евреите“.

Частният канал Rete 4 е от медийната империя на бившия премиер (и дългогодишен съюзник на Кремъл) Силвио Берлускони, който само преди дни заяви, че Путин е бил „принуден от руския народ и от партията и министрите си“ да проведе тази „специална операция“ в Украйна. След серия подобни интервюта с руския елит, властите в Италия реагираха, а медиите смениха курса и спряха да канят събеседници, свързани с Кремъл или разпространяващи разказа му.

Последвалите месеци, а и резултатите от скорошните избори в Италия – западноевропейската страна с най-съпоставими с България нагласи към Русия – обаче показаха, че промяната в редакционната политика на традиционните медии не решава проблема. Опасенията от трудна зима заради инфлацията и енергийната криза извадиха на преден план едно от най-коварните руски оръжия в условията на инфодемия: съмнението.

От Русия с болка

Италия изглежда подходяща за руски операции. През Студената война там действа най-мощната комунистическа партия в Западния блок. В края на тази пролет над една трета от италианците виждаха в Украйна и Запада най-голямата пречка пред мира (рекорд сред десетте държави, изследвани от Европейския съвет за външна политика), а едва малко над половината обвиняват Русия за войната. На власт бяха и се връщат някои защитници на Кремъл – освен Берлускони, такъв е и председателят на крайнодясната „Лига“ (побратимена с прокремълската „Единна Русия“) Матео Салвини.

На този фон осемнайсетте месеца власт на Марио Драги бяха болезнени за руския президент Владимир Путин предвид ярко проевропейските позиции (затова Москва коментира оставката на Драги с пожелание към италианците следващото им правителство да не е „слуга на интересите на САЩ“). През март руското Външно министерство заплаши Италия с „необратими последици“, ако бъдат приети още санкции.

Изглежда, че в руските пропагандни операции медиите в Италия паднаха първа жертва. Тъкмо в предаването Zona Bianca, където бе и интервюто с Лавров, известен италиански журналист, сражавал се преди години в Донбас, заяви за избиването на цивилни в Буча: „Имаше клане в Буча, но честно казано, не мога да кажа кой го направи. Нацистите са в Киев, където ги поставиха и някои представители на нашето правителство.“

Руският елит безкритично получаваше ефирно време, както отбелязва изследователят Матео Пулиезе. И то не само по Rete 4 на Берлускони. За италиански телевизии от началото на войната са коментирали и водещият рупор на Кремъл от „Первый канал“ Владимир Соловьов, и самият Александър Дугин, и символът на конфронтационния, изпълнен със съмнителни твърдения подход на Русия срещу Запада през последните години – говорителката на руското Външно министерство Мария Захарова (италиански журналист пътува до Москва за разговор с нея).

Редом с руснаците по телевизиите се изявяват и италианци в защита на Русия. Социологът Алесандро Орсини (противник на изпращането на оръжия на Украйна) твърди без никакви доказателства, че майки от Мариупол му изпращали писма всеки ден, молейки го да даде гласност, че украинците не искат война, а италианците са „луди“ да изпращат оръжия. Обществената медия Rai 1 показа „карта“ на стоманодобивния конгломерат „Азовстал“, където бяха последните сражения преди пълното овладяване на града от Русия и където според предаването били „биолабораториите“ на САЩ в Украйна. Всъщност „картата“, подхваната и от други медии, представлява разпространяван от руския политолог Сергей Марков 3D модел на недовършена настолна игра.

„В началото няколко лидери на мнение и геополитически анализатори бяха убедени в легитимността на действията на Русия [смятайки, че тя е била провокирана от НАТО]“, разказва в телефонен разговор Арие Антинори, професор по криминология и социология на девиантното поведение в Римския университет „Сапиенца“, сред чиито теми на изследване са информационните войни онлайн. „Имаше виктимизация и подкрепа за самовиктимизацията на Русия. Някои учени, журналисти, коментатори и политици участваха в дебата за тази перспектива“, допълва той.

Италианските власти реагираха, руснаците и пригласящите им изчезнаха от ефира, но не и дезинформацията.

Тласък вместо пропаганда

„Алтернативните“ онлайн медии и публикациите с фалшиви новини са само част от обяснението – например 34-те блога, превеждащи „алтернативна информация“ от руската платформа News Front (която има и българска секция), описани в изследване на аналитичния отдел на вестник Corriere della Sera. В помощ на руските операции бе и най-голямата от 24 февруари насам мрежа от имитиращи европейски медии сайтове с фалшиви новини за Украйна, бежанците и санкциите, чиито постове във Facebook са премахнати от „Мета“ преди седмица.

В доклад от миналия месец италианската парламентарна комисия за сигурност COPASIR съзира „съществена слабост в действията за борба с дезинформацията и различните форми на намеса“. В доклада, който обхваща периода февруари–август 2022 г., се казва също, че „Италия заради своята история и географско положение може да бъде инструмент за натиск върху евро-атлантизма и за отслабване на средиземноморската му проекция в полза на нарастващото руско стратегическо присъствие в Северна Африка, Сахел и на Балканите“.

Мария Захарова показа, че разбира това. С действията си тя обрисува и оръжието, срещу което сегашните стратегии се оказаха безсилни. В началото на септември, три седмици преди парламентарните избори в Италия, Захарова се обърна директно към италианците. В Telegram тя нападна приетия ден по-рано национален план за пестене на енергия, твърдейки, че зимата ще е тежка и студена, защото италианците са марионетки, управлявани от Вашингтон (който всъщност налагал политиките на Брюксел). Италианската икономика била в плен и вървяла към самоубийство заради „санкционното безумие“, докато американските бизнесмени плащали за енергия седем пъти по-малко. В последвалите коментари имаше и отговори на италиански. Външният министър Луиджи ди Майо нарече коментарите „намеса“ в суверенитета на Италия (за разлика от Салвини, който ги подкрепи).

Думите на Захарова отекнаха силно в италианските медии. Именно енергийната криза се утвърди като водеща тема в руските операции и в Италия – включително защото тя е реалност (и е в дневния ред на обществото). Според анализ на EURACTIV темата присъства ясно в проруски публикации в социалните мрежи в предизборната кампания: повишаването на цените не е дело на самия президент Владимир Путин, виновникът е ЕС заради оръжията и санкциите спрямо Русия. Дори само говоренето за пускане и спиране на газопроводи се превръща в оръжие. Европейската платформа EUvsDisinfo обърна внимание този месец на публикация на изданието L’AntiDiplomatico, което подкрепи Захарова за предстоящото „икономическо самоубийство“.

Директното взаимодействие с италианците дава достъп до публиката „отдолу нагоре“. Това е подход от ХХI, а не от ХХ век, смята проф. Арие Антинори. Той изследва Twitter, Telegram и други алтернативни мрежи, като Gab, в които крайни групи смесват теми от Украйна с въпросите за „джендъра“, мигрантите и ислямофобията. Участниците в тях са малцинство сред италианците. Достигат ли обаче посланията им до мнозинството, ако, както проф. Антинори сам твърди, за Русия е важно да се цели в тъканта на обществото с подход „да разрушава демокрацията от самите недра на всяка държава“? Въз основа на наблюденията си той стига до извода, че на руските операции не е нужна тълпа, а само критична маса.

„Днес проблемът не е как да достигнете до масите“, обяснява проф. Антинори. Той смята, че пропагандата (чийто латински корен означава „разпространение“) е понятие, свързано с XIX и XX век. Вместо това той говори за „тласък“ (propulsion). Целта на „тласъка“ са „глобализираните индивиди“, податливи на влияние и способни да се събират в малки групи, в свързани (connective), а не колективни (collective) общности.

„Не е нужно да преобърнете поведението или мисленето на италианците, както става с германците при нацистите […] Ако искате да създадете реакция, не ви трябват милиони хора. Нужни са само няколко събития, малко безредици – в Будапеща, в Прага, в Берлин, в Рим, във Франция“, казва Антинори. Властта на манипулиращия идва не от поляризацията, а от „едно от най-значимите оръжия в съвременния сценарий: съмнението“. Не е нужно човек да е срещу правителството (и срещу водещия дискурс) или да бъде убеден в подкрепа на Русия: да се поддържат хората постоянно поляризирани е трудно, да се манипулират емоциите им – по-лесно. Достатъчно е да се съмняват във властта и политическата система, да бъдат провокирани и да се замислят дали санкциите не вредят на Италия, обобщава изследователят.

Мълчаливата третина

Ефектът от съмнението не беше очевиден на изборите: подчертано проруската „Лига“ стопи подкрепата си. Нима руските операции не бяха достатъчни, за да повлияят на изборите? „Не бяха достатъчни, но бяха полезни за подсилване на ключовите играчи на тези избори – хората, които решиха да не гласуват. Въздържаха се 36%, в някои южни райони – до 50%. Над една трета от населението не беше представена“, коментира Арие Антинори. Числото едва ли впечатлява България с по-ниската ѝ избирателна активност, но за Италия беше отрицателен рекорд.

Изследванията на професора показват повишена предизборна активност тъкмо на групи, подкопаващи доверието във властта: не чрез опозиция на базата на сблъсък на идеи или алтернативи, а само на съмнение. В Италия (както и в другите две мишени на руската пропаганда – Германия и Източна Европа) „е нужно да се създаде критична маса, не масово движение срещу правителството“. Според него това е сериозен риск, защото някои от тях може да твърдят, че говорят от името на тези 36% негласували италианци.

На въпроса дали очаква руски натиск за сваляне на санкциите и операции за влияние тази есен, проф. Антинори отговаря утвърдително. Той допуска и че в условията на икономическа криза натискът на гражданите по темата може да влезе в дневния ред на правителството на десницата. В следващите месеци влиянието на играта с емоциите на споменатите „свързани общности“ върху общественото мнение може да има решаващо значение за политиката на Италия. А и за цяла Европа.

Заглавна илюстрация: © Пеню Кирацов
„Тоест“ е официален партньор за публикуването на материалите от поредицата „Хроники на инфодемията“, реализирана от АЕЖ-България съвместно с Фондация „Фридрих Науман“.

Източник

The collective thoughts of the interwebz