If you really-really need to, it is quite possible to create a privileged helper program that can be run via fork()+exec(), using an Unix domain stream socket pair to pass back the data.
Why a socket and not a normal pipe (so it could be run via popen())?
Because if you really need to do this, then you also need to add security checks. If the parent process creates an Unix domain stream socket pair connecting the standard output of the privileged helper and itself, the privileged helper can verify the parent process identity using getsockopt(STDOUT_FILENO, SOL_SOCKET, SO_PEERCRED, &creds, &credslen) with struct ucred creds; socklen_t credslen = sizeof creds;. If it returns zero and credslen == sizeof creds, the parent process ID is creds.pid (which obviously should match getppid()), and the user and group the parent process is running as in creds.uid and creds.pid. Furthermore, the privileged helper can then verify which binary the parent process is running by calling readlink() on /proc/PID/exe.
A common attack in such mechanisms (they're used often with web services; see e.g. Apache SUExec etc.) is to entice the parent process to execute another (untrusted, hacked) binary just after it has forked and executed the helper and the helper has verified the parent. These can be avoided by having the parent create the socket pair with SOCK_CLOEXEC flags (which the forked child process must undo for its own end before exec'ing the child process), and write a single-byte message after it has closed the end it gave the child process. The helper waits for the message before verifying the parent process. This way, the socket pair connection cannot leak outside the parent process without the child detecting it, unless someone manages to inject code into the parent process that deliberately leaks that socket pair connection.
This model is not itself secure, unless the parent process binary is immutable for all non-root users (i.e., installed as root, as binaries typically are, with only execute access to other users and groups). If both the parent binary and the privileged helper binary are only writable by root, then this pattern is quite robust; code injection vulnerabilities can still break it, though – but those can break absolutely any process anyway. Because a socket pair is used, the communication cannot be observed by any other processes running on the machine. Because the privileged child process will only check the parent process identity after the parent process has secured its side of the connection, the connection cannot leak outside the parent process without the child process detecting it happened; there is no race window.
Code-wise, it's just a couple of hundred lines of codes that takes a couple of days to write (well, one day to write, the next day to verify and check with fresh eyes).
As a pattern, it is reliable within the parameters outlined above. But it isn't something you throw at a problem because the proper solutions are less effort; that attitude just leads to pwned machines. (As an example, if someone tells you to run chmod 0777 /var/www/html, that's like telling people to leave their doors unlocked because burglaries are just a conspiracy theory and do not happen in real life, and anyway keeping the doors unlocked might save you a fraction of a second getting out if your house caught on fire.)