Guard reqwest::proxy libtests against concurrent ENV modification
As ENV is process global, modifying it within a thread (as is normal for all test targets in a rust libtest) results in a concurrency data-race. This patch fences the two known cases of needing to modify this by locking all ENV modifications, and collection of data dependent on said modifications, into a narrow path isolated by a Mutex lock, with no test assert!()'s while the Mutex is held ( to avoid a Mutex Posioning ). However, the code doesn't treat lock failure as a special circumstance, and if the lock fails, then the pre-existing risk of conccurent ENV modification returns, and these 2 tests can still randomly fail, but _in that situation_. And as mutexes can _only_ be poisoned by the 2 threads holding this mutex, this regression can now only trip into concurrency issues when either of these 2 tests are already failing from _non test_ assertions, so this patch still improves the status quo substantially. Closes: https://github.com/seanmonstar/reqwest/issues/829
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							b42d2e0e0c
						
					
				
				
					commit
					6a41459862
				
			
							
								
								
									
										60
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -691,6 +691,8 @@ fn get_from_registry() -> SystemProxyMap { | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::sync::Mutex; | ||||
|     use lazy_static::lazy_static; | ||||
|  | ||||
|     impl Dst for Url { | ||||
|         fn scheme(&self) -> &str { | ||||
| @@ -799,47 +801,75 @@ mod tests { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Smallest possible content for a mutex | ||||
|     struct MutexInner; | ||||
|  | ||||
|     lazy_static! { | ||||
|         static ref ENVLOCK: Mutex<MutexInner> = { | ||||
|             Mutex::new(MutexInner) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_sys_proxies_parsing() { | ||||
|         // Stop other threads from modifying process-global ENV while we are. | ||||
|         let _lock = ENVLOCK.lock(); | ||||
|         // save system setting first. | ||||
|         let _g1 = env_guard("HTTP_PROXY"); | ||||
|         let _g2 = env_guard("http_proxy"); | ||||
|  | ||||
|         // empty | ||||
|         assert_eq!(get_sys_proxies().contains_key("http"), false); | ||||
|  | ||||
|         // Mock ENV, get the results, before doing assertions | ||||
|         // to avoid assert! -> panic! -> Mutex Poisoned. | ||||
|         let baseline_proxies = get_sys_proxies(); | ||||
|         // the system proxy setting url is invalid. | ||||
|         env::set_var("http_proxy", "123465"); | ||||
|         assert_eq!(get_sys_proxies().contains_key("http"), false); | ||||
|  | ||||
|         let invalid_proxies = get_sys_proxies(); | ||||
|         // set valid proxy | ||||
|         env::set_var("http_proxy", "http://127.0.0.1/"); | ||||
|         let proxies = get_sys_proxies(); | ||||
|  | ||||
|         let p = &proxies["http"]; | ||||
|         assert_eq!(p.scheme(), "http"); | ||||
|         assert_eq!(p.host(), "127.0.0.1"); | ||||
|         let valid_proxies = get_sys_proxies(); | ||||
|  | ||||
|         // reset user setting when guards drop | ||||
|         drop(_g1); | ||||
|         drop(_g2); | ||||
|         // Let other threads run now | ||||
|         drop(_lock); | ||||
|  | ||||
|         assert_eq!(baseline_proxies.contains_key("http"), false); | ||||
|         assert_eq!(invalid_proxies.contains_key("http"), false); | ||||
|  | ||||
|         let p = &valid_proxies["http"]; | ||||
|         assert_eq!(p.scheme(), "http"); | ||||
|         assert_eq!(p.host(), "127.0.0.1"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_get_sys_proxies_in_cgi() { | ||||
|         // Stop other threads from modifying process-global ENV while we are. | ||||
|         let _lock = ENVLOCK.lock(); | ||||
|         // save system setting first. | ||||
|         let _g1 = env_guard("REQUEST_METHOD"); | ||||
|         let _g2 = env_guard("HTTP_PROXY"); | ||||
|  | ||||
|  | ||||
|         // Mock ENV, get the results, before doing assertions | ||||
|         // to avoid assert! -> panic! -> Mutex Poisoned. | ||||
|         env::set_var("HTTP_PROXY", "http://evil/"); | ||||
|  | ||||
|         // not in CGI yet | ||||
|         assert_eq!(get_sys_proxies()["http"].host(), "evil"); | ||||
|  | ||||
|         let baseline_proxies = get_sys_proxies(); | ||||
|         // set like we're in CGI | ||||
|         env::set_var("REQUEST_METHOD", "GET"); | ||||
|         assert!(!get_sys_proxies().contains_key("http")); | ||||
|  | ||||
|         let cgi_proxies = get_sys_proxies(); | ||||
|  | ||||
|         // reset user setting when guards drop | ||||
|         drop(_g1); | ||||
|         drop(_g2); | ||||
|         // Let other threads run now | ||||
|         drop(_lock); | ||||
|  | ||||
|         // not in CGI yet | ||||
|         assert_eq!(baseline_proxies["http"].host(), "evil"); | ||||
|         // In CGI | ||||
|         assert!(!cgi_proxies.contains_key("http")); | ||||
|     } | ||||
|  | ||||
|     /// Guard an environment variable, resetting it to the original value | ||||
|   | ||||
		Reference in New Issue
	
	Block a user