When trying to compile this code
#include <stdarg.h>
void bar_ptr(int n, va_list *pvl) {
    // do va_arg stuff here
}
void bar(int n, va_list vl) {
    va_list *pvl = &vl; // error here
    bar_ptr(n, pvl);
}
void foo(int n, ...) {
    va_list vl;
    va_list *pvl = &vl; // fine here
    va_start(vl, n);
    bar(n, vl);
    va_end(vl);
}
int main() {
    foo(3, 1, 2, 3);
    return 0;
}
the GCC compiler prints a warning about initialization from incompatible pointer type in the bar function. The identical statement is fine in foo.
It seems that the type of an agument of type va_list is not a va_list. This can be tested easily with a static assertion like
_Static_assert(sizeof(vl) == sizeof(va_list), "invalid type");
in the bar function. With GCC, the _Static_assert fails. The same can be tested also in C++ with declytpe and std::is_same.
I would like to take the address of the va_list vl argument of bar, and pass it as argument of bar_ptr, to do thinks like the one described in this thread. On the other hand, it is fine to call bar_ptr(n, pvl) directly from main, replacing bar(n, vl).
According to the footnote 253 of the C11 final draft,
It is permitted to create a pointer to a
va_listand pass that pointer to another function
Why this cannot be done if va_list is defined as argument of the function, and not in the function body?
Workaround:
Even if this does not answer the question, a possible workaround is to change the content of bar by using a local copy of the argument created with va_copy:
void bar(int n, va_list vl) {
    va_list vl_copy;
    va_copy(vl_copy, vl);
    va_list *pvl = &vl_copy; // now fine here
    bar_ptr(n, pvl);
    va_end(va_copy);
}