Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apache Segmentation fault (11) disappears after print_r on a variable. can't understand... #10430

Open
pfrappe opened this issue Jan 24, 2023 · 34 comments

Comments

@pfrappe
Copy link

pfrappe commented Jan 24, 2023

Description

Sorry this problem appears within a big application and I didn't find a short php code reproducing the problem.
So I try to put the maximum explanations here.

In some cases I get this message in error log :
[core:notice] [pid 910] AH00051: child pid 23361 exit signal Segmentation fault (11), possible coredump in /etc/apache2

I found this message always appears twice because, when I get it, the index.php is executed a second time.
To find that, I put an errog_log('deb') at the very beginning of the index.php and an error_log('fin') at the last instruction.
In the log, I got :

[Tue Jan 24 10:50:37.515447 2023] [php:notice] [pid 23488] [client 127.0.0.1:49088] deb, referer: http://localhost/wer/index.php
[Tue Jan 24 10:50:40.193277 2023] [php:notice] [pid 23488] [client 127.0.0.1:49088] fin, referer: http://localhost/wer/index.php
[Tue Jan 24 10:50:40.856392 2023] [php:notice] [pid 23835] [client 127.0.0.1:49102] deb, referer: http://localhost/wer/index.php

[Tue Jan 24 10:50:41.479629 2023] [core:notice] [pid 910] AH00051: child pid 23488 exit signal Segmentation fault (11), possible coredump in /etc/apache2
[Tue Jan 24 10:50:43.102831 2023] [php:notice] [pid 23835] [client 127.0.0.1:49102] fin, referer: http://localhost/wer/index.php
[Tue Jan 24 10:50:43.482049 2023] [core:notice] [pid 910] AH00051: child pid 23835 exit signal Segmentation fault (11), possible coredump in /etc/apache2

So, index.php seems to be executed twice.
However, if I error_log the expected HTML result in the log, it is totally correct. (and I get it twice...)
But nothing is sent to the navigator.

Bypass

Step by step, I found a bypass : the problem disappears when I put a print_r of some object at the right place.
For example :

This caused the problem :

$ret = SG_Operation::execFonctionSansParametre($this, $o, $nom, $p2, $p1, $contexte);
//print_r($ret, true);

And this works correctly :

$ret = SG_Operation::execFonctionSansParametre($this, $o, $nom, $p2, $p1, $contexte);
print_r($ret, true);

It works too if I add :

$ret = unserialize(serialize($ret);

If the dumps of the object before and after the problem are identical.

I tried some other features which don't work :

$obj = SG_Operation::execFonctionSansParametre($this, $o, $nom, $p2, $p1, $contexte);
$ret = $obj;

or

$obj = SG_Operation::execFonctionSansParametre($this, $o, $nom, $p2, $p1, $contexte);
$ret = clone $obj;

Research notes

I found that the bypass does work if I just add the '''print_r''' as the first line of the __construct function of one of my classes :

/** SynerGaia contient la classe SG_Nombre de traitement des nombres */
defined("SYNERGAIA_PATH_TO_ROOT") or die('403.14 - Directory listing denied.');

/** Pour ajouter les méthodes et propriétés spécifiques de l'application créées par le compilateur */
if (file_exists(SYNERGAIA_PATH_TO_VAR . 'SG_Nombre_trait.php')) {
    include_once SYNERGAIA_PATH_TO_VAR . 'SG_Nombre_trait.php';
} else {
    /** trait vide par défaut */
    trait SG_Nombre_trait{};
}
/**
 * SG_Nombre : Classe SynerGaia de gestion d'un nombre
 * @since 0.0
 * @version 2.9
 */
class SG_Nombre extends SG_Objet {
    // complément de classe créée par compilation
    use SG_Nombre_trait;

    /** string Type SynerGaia '@Nombre' */
    const TYPESG = '@Nombre';
    
    /** string Type SynerGaia  */
    public $typeSG = self::TYPESG;

    /** integer|float Valeur interne du nombre (null par défaut) */
    public $valeur;
    
    /** string Unité de mesure (pas encore géré) */
    public $unite = '';

    /**
     * Construction de l'objet
     * @since 1.0.7
     * @version 2.4 si rien : 0
     * @version 2.9.1 getTexte pour unité
     * @param any $pQuelqueChose valeur à partir de laquelle le SG_Nombre est créé
     * @param any $pUnite code unité ou ou objet @Unite de la quantité
     */
    function __construct($pQuelqueChose = null, $pUnite = null)
    {
print_r($this, true); // bypass bug #10430
        $tmpTypeSG = SG_Objet::getTypeSG($pQuelqueChose);
        switch ($tmpTypeSG) {
            case 'integer' :
                $this -> valeur = (double)$pQuelqueChose;
                break;
            case 'double' :
                $this -> valeur = $pQuelqueChose;
                break;
            case 'string' :
                if ($pQuelqueChose !== '') {
                    $floatString = $pQuelqueChose;
                    $floatString = str_replace(" ", "", $floatString);
                    $floatString = str_replace(",", ".", $floatString);
                    $this -> valeur = floatval($floatString);
                } else {
                    $this -> valeur = null;
                }
                break;
            case '@Formule' :
                $tmpNombre = new SG_Nombre($pQuelqueChose -> getResult());
                $this -> valeur = $tmpNombre -> valeur;
                break;
            case '@Nombre' :
                $this -> valeur = $pQuelqueChose -> valeur;
                $this -> unite = $pQuelqueChose -> unite;
                break;
            case 'NULL' :
                $this -> valeur = 0; //null;
                break;
            default :
                // Si objet SynerGaia
                if (substr($tmpTypeSG, 0, 1) === '@') {
                    $this -> valeur = $pQuelqueChose -> toFloat();
                }
        }
        if($pUnite) {
            $this -> unite = SG_Texte::getTexte($pUnite);
        }
    }

I also remark when the bypass works, the new class has been called by an instruction like :

$class = "MyClass";
$obj = new $class();

But, I can't make a short PHP program reproducing the problem.

Environment

PHP 8.1.2-1ubuntu2.10 (cli) (built: Jan 16 2023 15:19:49) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.2-1ubuntu2.10, Copyright (c), by Zend Technologies

on Ubuntu 22.04.1 LTS

I hope I'm clear enough clear...
Pierre Frappé (France)

PHP Version

first PHP 8.1.2, and now 8.2.1 (no change)
JIT tracing

Operating System

Ubuntu 22.04.1 LTS

@damianwadley
Copy link
Member

Are you able to get and inspect that core dump? Do you have any third-party PHP extensions installed?

@pfrappe
Copy link
Author

pfrappe commented Jan 24, 2023 via email

@nielsdos
Copy link
Member

Are you using the JIT, if yes, does the bug also occur with JIT disabled?

@pfrappe
Copy link
Author

pfrappe commented Jan 24, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 24, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 24, 2023 via email

@nielsdos
Copy link
Member

It would be ideal if we could get a stacktrace. Are you able to attach gdb to the child process that crashes (well... before it actually crashes)?

@pfrappe
Copy link
Author

pfrappe commented Jan 24, 2023 via email

@alecpl
Copy link

alecpl commented Jan 25, 2023

8.1.2 is quite old, I suggest an update.

@pfrappe
Copy link
Author

pfrappe commented Jan 25, 2023 via email

@smokefire69

This comment was marked as spam.

@pfrappe
Copy link
Author

pfrappe commented Jan 25, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 25, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 26, 2023 via email

@nielsdos
Copy link
Member

nielsdos commented Jan 26, 2023

Ah right, I see... I believe you can however use gdb on Apache and follow its children with the command set follow-fork-mode child. When you observe the crash in gdb you can try to get a backtrace with the bt command. Are you willing to give that a try? :) Giving us a backtrace and the error will help us find the issue.

@pfrappe
Copy link
Author

pfrappe commented Jan 27, 2023 via email

@nielsdos
Copy link
Member

Thanks for the effort you already put into the issue!
It's almost what we need. Now you have the stacktrace at the moment of attaching, and we need the stacktrace at the moment of the crash.
You have to do the following steps:

  1. attach gdb to apache the same way as you did now
  2. set gdb to follow the child process using: set follow-fork-mode child
  3. tell gdb to let apache continue using: continue
  4. trigger the crash in php, when it crashes gdb will pause the process
  5. print the backtrace using: bt

@pfrappe
Copy link
Author

pfrappe commented Jan 28, 2023 via email

@nielsdos
Copy link
Member

Thank you. It looks like a stack overflow due to infinite recursion between two functions in php.
Are you able to redo what you just did, but with some extra steps?

  1. in bash execute: export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com"
  2. start gdb, but before attaching type: set debuginfod enabled on
  3. now follow the same steps from my previous post (seting the follow fork mode, attaching, etc...)
    If gdb asks you if you want to use debuginfod answer y
    This should hopefully give us more than just addresses to work with.

@nielsdos
Copy link
Member

I see you updated your OP to say you're using tracing JIT, I thought you weren't using the JIT. Are you able to try if the bug still occurs after disabling the JIT?

@pfrappe
Copy link
Author

pfrappe commented Jan 28, 2023 via email

@nielsdos
Copy link
Member

The debug info isn't there with the way you did it.
What I meant with "in bash" is to execute the export command on the command line.
To be fully clear I'll give the full steps here:

On the command line execute the following commands one after another:

  1. export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com"
  2. ps -elf | grep apache2 to find the process ID of apache
  3. sudo gdb so you get into gdb, everything from now on will be entered in gdb
  4. set debuginfod enabled on
  5. set follow-fork-mode child
  6. attach PROCESS_ID_OF_APACHE_HERE
  7. continue
  8. trigger the crash in php
  9. bt

@pfrappe
Copy link
Author

pfrappe commented Jan 28, 2023 via email

@nielsdos
Copy link
Member

Weird that it doesn't respond to bt. Did you do bt after the crash? Is it showing any error at all?

@pfrappe
Copy link
Author

pfrappe commented Jan 28, 2023 via email

@nielsdos
Copy link
Member

It's hard to say, I feel like you're attaching to the wrong process, or I'm missing something.
I'm guessing your application is closed source so I can't test it on my own machine?

@pfrappe
Copy link
Author

pfrappe commented Jan 28, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 28, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 30, 2023 via email

@pfrappe
Copy link
Author

pfrappe commented Jan 30, 2023 via email

@nielsdos
Copy link
Member

Thank you for all the effort you put into this, I really appreciate that a lot. I'm not going to ask anything more from you, like as you said you already put alot of time into it.
I'll see what I can do with the info I have, there are certainly some clues even though there isn't a full reproducer.
Thanks

@crrodriguez
Copy link
Contributor

Can you reproduce it using the FPM sapi .. it should be pretty easy to setup in Ubuntu 22.04.1 LTS as it ships with an apache config file for that..

@pfrappe
Copy link
Author

pfrappe commented Feb 15, 2023 via email

@crrodriguez
Copy link
Contributor

Are PHP-FPM and FPM sapi the same ?

Yes.

In recent ubuntu releases it should be a matter of installing php-fpm
a2enconf php8.2-fpm
a2dismod php8.2
systemctl restart php8.2-fpm
systemctl restart apache2

You probably want something to catch you core dumps. you need to install systemd-coredump package and reboot.. then with coredumpctl see all crashes and get backtraces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants