VYPR
High severityNVD Advisory· Published Nov 19, 2019· Updated Aug 6, 2024

CVE-2012-6135

CVE-2012-6135

Description

RubyGems passenger 4.0.0 betas 1 and 2 allows remote attackers to delete arbitrary files during the startup process.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
passengerRubyGems
< 4.0.0.rc44.0.0.rc4

Affected products

1

Patches

1
8c6693e08187

Security check socket filenames reported by spawned application processes.

https://github.com/phusion/passengerHongli Lai (Phusion)Jan 31, 2013via ghsa
1 file changed · +92 6
  • ext/common/ApplicationPool2/Spawner.h+92 6 modified
    @@ -60,6 +60,7 @@
     #include <boost/make_shared.hpp>
     #include <boost/shared_array.hpp>
     #include <boost/bind.hpp>
    +#include <boost/foreach.hpp>
     #include <oxt/system_calls.hpp>
     #include <oxt/backtrace.hpp>
     #include <sys/types.h>
    @@ -409,7 +410,9 @@ class Spawner {
     		}
     	}
     
    -	ProcessPtr handleSpawnResponse(NegotiationDetails &details) {
    +	ProcessPtr handleSpawnResponse(const SpawnPreparationInfo &preparation,
    +		NegotiationDetails &details)
    +	{
     		TRACE_POINT();
     		SocketListPtr sockets = make_shared<SocketList>();
     		while (true) {
    @@ -465,6 +468,13 @@ class Spawner {
     				vector<string> args;
     				split(value, ';', args);
     				if (args.size() == 4) {
    +					string error = validateSocketAddress(preparation, details, args[1]);
    +					if (!error.empty()) {
    +						throwAppSpawnException(
    +							"An error occurred while starting the web application. " + error,
    +							SpawnException::APP_STARTUP_PROTOCOL_ERROR,
    +							details);
    +					}
     					sockets->add(args[0],
     						fixupSocketAddress(*details.options, args[1]),
     						args[2],
    @@ -555,6 +565,76 @@ class Spawner {
     		}
     	}
     
    +	bool isAbsolutePath(const StaticString &path) const {
    +		if (path.empty() || path[0] != '/') {
    +			return false;
    +		} else {
    +			vector<string> components;
    +			string component;
    +
    +			split(path, '/', components);
    +			components.erase(components.begin());
    +			foreach (component, components) {
    +				if (component.empty() || component == "." || component == "..") {
    +					return false;
    +				}
    +			}
    +			return true;
    +		}
    +	}
    +
    +	/**
    +	 * Given a 'socket:' information string obtained from the spawned process,
    +	 * validates whether it is correct.
    +	 */
    +	string validateSocketAddress(const SpawnPreparationInfo &preparation,
    +		NegotiationDetails &details,
    +		const string &_address) const
    +	{
    +		string address = _address;
    +		stringstream error;
    +
    +		switch (getSocketAddressType(address)) {
    +		case SAT_UNIX: {
    +			address = fixupSocketAddress(*details.options, address);
    +			string filename = parseUnixSocketAddress(address);
    +
    +			// Verify that the socket filename is absolute.
    +			if (!isAbsolutePath(filename)) {
    +				error << "It reported a non-absolute socket filename: \"" <<
    +					cEscapeString(filename) << "\"";
    +				break;
    +			}
    +
    +			// Verify that the process owns the socket.
    +			struct stat buf;
    +			if (lstat(filename.c_str(), &buf) == -1) {
    +				int e = errno;
    +				error << "It reported an inaccessible socket filename: \"" <<
    +					cEscapeString(filename) << "\" (lstat() failed with errno " <<
    +					e << ": " << strerror(e) << ")";
    +				break;
    +			}
    +			if (buf.st_uid != preparation.uid) {
    +				error << "It advertised a Unix domain socket that has a different " <<
    +					"owner than expected (should be UID " << preparation.uid <<
    +					", but actual UID was " << buf.st_uid << ")";
    +				break;
    +			}
    +			break;
    +		}
    +		case SAT_TCP:
    +			// TODO: validate that the socket is localhost.
    +			break;
    +		default:
    +			error << "It reported an unsupported socket address type: \"" <<
    +				cEscapeString(address) << "\"";
    +			break;
    +		}
    +
    +		return error.str();
    +	}
    +
     	static void checkChrootDirectories(const Options &options) {
     		if (!options.preexecChroot.empty()) {
     			// TODO: check whether appRoot is a child directory of preexecChroot
    @@ -1001,7 +1081,7 @@ class Spawner {
     		}
     	}
     	
    -	ProcessPtr negotiateSpawn(NegotiationDetails &details) {
    +	ProcessPtr negotiateSpawn(const SpawnPreparationInfo &preparation, NegotiationDetails &details) {
     		TRACE_POINT();
     		details.spawnStartTime = SystemTime::getUsec();
     		details.gupid = integerToHex(SystemTime::get() / 60) + "-" +
    @@ -1043,7 +1123,7 @@ class Spawner {
     					details);
     			}
     			if (result == "Ready\n") {
    -				return handleSpawnResponse(details);
    +				return handleSpawnResponse(preparation, details);
     			} else if (result == "Error\n") {
     				handleSpawnErrorResponse(details);
     			} else {
    @@ -1184,10 +1264,14 @@ class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner>
     	// Protects everything else.
     	mutable boost::mutex syncher;
     
    +	// Preloader information.
     	pid_t pid;
     	FileDescriptor adminSocket;
     	string socketAddress;
     	unsigned long long m_lastUsed;
    +	// Upon starting the preloader, its preparation info is stored here
    +	// for future reference.
    +	SpawnPreparationInfo preparation;
     	
     	void onPreloaderOutputReadable(ev::io &io, int revents) {
     		char buf[1024 * 8];
    @@ -1318,7 +1402,7 @@ class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner>
     		
     		shared_array<const char *> args;
     		vector<string> command = createRealPreloaderCommand(options, args);
    -		SpawnPreparationInfo preparation = prepareSpawn(options);
    +		preparation = prepareSpawn(options);
     		SocketPair adminSocket = createUnixSocketPair();
     		Pipe errorPipe = createPipe();
     		DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
    @@ -1432,6 +1516,7 @@ class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner>
     			pid = -1;
     		}
     		socketAddress.clear();
    +		preparation = SpawnPreparationInfo();
     	}
     	
     	void sendStartupRequest(StartupDetails &details) {
    @@ -1529,6 +1614,7 @@ class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner>
     			string key = line.substr(0, pos);
     			string value = line.substr(pos + 2, line.size() - pos - 3);
     			if (key == "socket") {
    +				// TODO: validate socket address here
     				socketAddress = fixupSocketAddress(options, value);
     			} else {
     				throwPreloaderSpawnException("An error occurred while starting up "
    @@ -1871,7 +1957,7 @@ class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner>
     		details.options = &options;
     		details.forwardStderr = config->forwardStderr;
     		details.forwardStderrTo = config->forwardStderrTo;
    -		ProcessPtr process = negotiateSpawn(details);
    +		ProcessPtr process = negotiateSpawn(preparation, details);
     		P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
     			", pid=" << process->pid);
     		return process;
    @@ -2107,7 +2193,7 @@ class DirectSpawner: public Spawner {
     			{
     				this_thread::restore_interruption ri(di);
     				this_thread::restore_syscall_interruption rsi(dsi);
    -				process = negotiateSpawn(details);
    +				process = negotiateSpawn(preparation, details);
     			}
     			detachProcess(process->pid);
     			guard.clear();
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.