Browse Source

Fixing libfcgi bad free bug

Bugfix consist of replacing the magic wrapper instance of os.environ (
aitomatically call os.putenv) by a normal dict instance.
Yann Weber 4 years ago
parent
commit
f075806524
3 changed files with 67 additions and 89 deletions
  1. 54
    75
      src/pyworker.c
  2. 7
    12
      src/pyworker.h
  3. 6
    2
      src/responder.c

+ 54
- 75
src/pyworker.c View File

@@ -45,7 +45,7 @@ pid_t spawn(char* py_entrypoint, int wrk_id, int semid, int max_reqs)
45 45
 int work(char* py_entrypoint, int wrk_id, int semid, int max_reqs)
46 46
 {
47 47
 	PyObject *entry_fun, *pystdout_flush, *pystderr_flush,
48
-	         *py_setenv, *py_clrenv;
48
+	         *py_osmod;
49 49
 	int count, pipe_out[2], pipe_err[2], pipe_ctl[2], err, piper_status;
50 50
 	struct sigaction act;
51 51
 	sigset_t emptyset;
@@ -62,6 +62,7 @@ int work(char* py_entrypoint, int wrk_id, int semid, int max_reqs)
62 62
 	}
63 63
 	act.sa_handler = worker_piper_sighandler;
64 64
 	act.sa_mask = emptyset;
65
+	//act.sa_flags = SA_RESTART;
65 66
 	act.sa_flags = 0;
66 67
 	act.sa_restorer = NULL;
67 68
 
@@ -76,7 +77,15 @@ int work(char* py_entrypoint, int wrk_id, int semid, int max_reqs)
76 77
 	syslog(	LOG_INFO,
77 78
 		"Worker[%d] Python started", wrk_id);
78 79
 	
79
-	get_py_setenv(&py_setenv, &py_clrenv);
80
+	//importing os
81
+	py_osmod = PyImport_ImportModule("os");
82
+	if(!py_osmod)
83
+	{
84
+		syslog(LOG_ALERT, "Unable to import os module");
85
+		log_expt(LOG_ALERT);
86
+		Py_Exit(EXIT_PYERR);
87
+	}
88
+
80 89
 	// loading module
81 90
 	entry_fun = import_entrypoint(py_entrypoint);
82 91
 
@@ -92,6 +101,7 @@ int work(char* py_entrypoint, int wrk_id, int semid, int max_reqs)
92 101
 			"Worker[%d] request %d", wrk_id, count);
93 102
 		worker_piper_sigrcv = 0;
94 103
 		pipe(pipe_ctl); //TODO : check for pipe error
104
+		//PyOS_BeforeFork();
95 105
 		pid_t pid = fork();
96 106
 		if(pid < 0)
97 107
 		{
@@ -118,7 +128,8 @@ int work(char* py_entrypoint, int wrk_id, int semid, int max_reqs)
118 128
 			//printf("Content-type: text/html\r\n\r\nHello world !\n");
119 129
 			exit(1);
120 130
 		}
121
-		update_pyenv(py_setenv, py_clrenv);
131
+		//PyOS_AfterFork_Parent();
132
+		update_pyenv(py_osmod);
122 133
 		//TODO : check if pipe_ctl lock is really needed anymore
123 134
 		close(pipe_ctl[1]);
124 135
 		PyObject_CallObject(entry_fun, NULL);
@@ -146,7 +157,7 @@ syslog(LOG_DEBUG, "PIPER UNLOCK");
146 157
 		syslog(LOG_DEBUG, "Worker[%d] request %d END [OK]",
147 158
 			wrk_id, count);
148 159
 	}
149
-	Py_Exit(count == max_reqs ?0:42);
160
+	Py_Exit(count == max_reqs ?0:EXIT_PYERR);
150 161
 }
151 162
 
152 163
 void worker_piper(int wrk_id, int req_id, int pystdout, int pystderr,
@@ -196,27 +207,23 @@ syslog(LOG_DEBUG, "Worler[%d] req #%d poll_ret = %d", wrk_id, req_id, poll_ret);
196 207
 			{
197 208
 				syslog(LOG_DEBUG, "Worker[%d] req #%d POLLIN STDOUT !",
198 209
 				       wrk_id, req_id);
199
-				while(1)
210
+				ret = read(pystdout, buf, PIPE_BUF);
211
+				syslog(LOG_DEBUG, "Worker[%d] req #%d read(stdout) ret %d",
212
+				       wrk_id, req_id, ret);
213
+				if(ret < 0)
200 214
 				{
201
-					ret = read(pystdout, buf, PIPE_BUF);
202
-					syslog(LOG_DEBUG, "Worker[%d] req #%d read(stdout) ret %d",
203
-					       wrk_id, req_id, ret);
204
-					if(ret < 0)
215
+					err = errno;
216
+					if(err == EINTR)
205 217
 					{
206
-						err = errno;
207
-						if(err == EINTR)
208
-						{
209
-							continue;
210
-						}
211
-						syslog( LOG_ERR,
212
-						        "Error reading python stdout : %s",
213
-							strerror(err));
214
-						exit(err);
218
+						continue;
215 219
 					}
216
-					buf[ret] = '\0';
217
-					printf("%s", buf);
218
-					break;
220
+					syslog( LOG_ERR,
221
+						"Error reading python stdout : %s",
222
+						strerror(err));
223
+					exit(err);
219 224
 				}
225
+				buf[ret] = '\0';
226
+				printf("%s", buf);
220 227
 			}
221 228
 			//TODO handle other poll events
222 229
 		}
@@ -262,7 +269,7 @@ syslog(LOG_DEBUG, "Worler[%d] req #%d poll_ret = %d", wrk_id, req_id, poll_ret);
262 269
 void worker_piper_sighandler(int signum)
263 270
 {
264 271
 	worker_piper_sigrcv = 1;
265
-	syslog(LOG_DEBUG, "SIG");
272
+	//syslog(LOG_DEBUG, "SIG");
266 273
 }
267 274
 
268 275
 PyObject* import_entrypoint(char* py_entrypoint)
@@ -496,7 +503,6 @@ void update_python_fd(int pipe_out[2], int pipe_err[2])
496 503
 				"Unable to fetch os.fdopen() , got NULL.");
497 504
 		}
498 505
 		err_fmt = NULL;
499
-		Py_DECREF(os_mod);
500 506
 		goto update_python_fd_err_pipes;
501 507
 	}
502 508
 
@@ -577,17 +583,29 @@ update_python_fd_err:
577 583
 }
578 584
 
579 585
 
580
-void update_pyenv(PyObject *py_setenv, PyObject *py_clrenv)
586
+void update_pyenv(PyObject *py_osmod)
581 587
 {
582
-	PyObject *args, *pykey, *pyval, *ret;
588
+	PyObject *pyenv, *pykey, *pyval;
583 589
 	char *key, *value, **cur;
584 590
 
585 591
 	cur = environ;
586 592
 
587
-	PyObject_CallObject(py_clrenv, NULL); // call os.environ.clear()
593
+	pyenv = PyObject_GetAttrString(py_osmod, "environ");
594
+	if(!pyenv)
595
+	{
596
+		syslog(LOG_WARNING, "Unable to get os.environ");
597
+		log_expt(LOG_ALERT);
598
+	}
599
+	else
600
+	{
601
+		Py_DECREF(pyenv);
602
+	}
603
+	pyenv = PyDict_New();
604
+
605
+
606
+
588 607
 	while(*cur)
589 608
 	{
590
-		//key = value = strdup(*cur);
591 609
 		key = value = *cur;
592 610
 		while(*value && *value != '=')
593 611
 		{
@@ -613,67 +631,28 @@ syslog(LOG_DEBUG, "PySetEnv '%s'='%s'", key, value);
613 631
 			Py_Exit(EXIT_PYERR);
614 632
 		}
615 633
 		*(value-1) = '='; // **environ restore
616
-		pyval = PyUnicode_DecodeLocale(value, "surrogateescape");
617
-		if(!pykey)
634
+		pyval = PyUnicode_DecodeFSDefault(value);
635
+		if(!pyval)
618 636
 		{
619 637
 			syslog(LOG_ALERT, "Unable to parse environ val string '%s'",
620 638
 			       value);
621 639
 			log_expt(LOG_ALERT);
622 640
 			Py_Exit(EXIT_PYERR);
623 641
 		}
624
-		args = Py_BuildValue("OO", pykey, pyval);
642
+		if(PyDict_SetItem(pyenv, pykey, pyval) == -1)
643
+		{
644
+			syslog(LOG_ERR, "Unable to set environ '%s'='%s'",
645
+			       key, value);
646
+			log_expt(LOG_ERR);
647
+		}
625 648
 		Py_DECREF(pyval);
626 649
 		Py_DECREF(pykey);
627 650
 		cur++;
628
-		ret = PyObject_CallObject(py_setenv, args);
629
-		if(ret)
630
-		{
631
-			Py_DECREF(ret);
632
-		}
633
-		if(PyErr_Occurred())
634
-		{
635
-			log_expt(LOG_WARNING);
636
-		}
637
-		//free(key);
638 651
 	}
652
+	PyObject_SetAttrString(py_osmod, "environ", pyenv);
639 653
 
640 654
 }
641 655
 
642
-void get_py_setenv(PyObject** pyenv_setitem, PyObject** pyenv_clear)
643
-{
644
-	PyObject *osmod, *pyenv;
645
-	osmod = PyImport_ImportModule("os");
646
-	if(!osmod)
647
-	{
648
-		syslog(LOG_ALERT, "Unable to import os module");
649
-		log_expt(LOG_ALERT);
650
-		Py_Exit(EXIT_PYERR);
651
-	}
652
-	pyenv = PyObject_GetAttrString(osmod, "environ");
653
-	if(!pyenv)
654
-	{
655
-		syslog(LOG_ALERT, "Unable to get os.environ");
656
-		log_expt(LOG_ALERT);
657
-		Py_Exit(EXIT_PYERR);
658
-	}
659
-	Py_DECREF(osmod);
660
-	*pyenv_setitem = PyObject_GetAttrString(pyenv, "__setitem__");
661
-	if(!*pyenv_setitem)
662
-	{
663
-		syslog(LOG_ALERT, "Unable to get os.environ.__setitem__");
664
-		log_expt(LOG_ALERT);
665
-		Py_Exit(EXIT_PYERR);
666
-	}
667
-	*pyenv_clear = PyObject_GetAttrString(pyenv, "clear");
668
-	if(!*pyenv_clear)
669
-	{
670
-		syslog(LOG_ALERT, "Unable to get os.environ.clear()");
671
-		log_expt(LOG_ALERT);
672
-		Py_Exit(EXIT_PYERR);
673
-	}
674
-	Py_DECREF(pyenv);
675
-}
676
-
677 656
 void log_expt(int priority)
678 657
 {
679 658
 	if(!PyErr_Occurred())

+ 7
- 12
src/pyworker.h View File

@@ -100,19 +100,14 @@ void update_python_path();
100 100
  */
101 101
 void update_python_fd(int[2], int[2]);
102 102
 
103
-/**@brief Update python sys.environ using current FCI environ
104
- * @note For the moment do not delete unset variables only update
105
- * from environ and add new one
106
- * @param PyObject* os.environ.__setitem__
107
- * @param PyObject* os.environ.clean
103
+/**@brief Clear then update python sys.environ using current FCI environ
104
+ * @note The environ has to be set without a call to os.putenv, the problem
105
+ * is that the os.environ is a special mapping calling putenv on setitem...
106
+ * For these reason the os.environ will be replaced by a new dict instance for
107
+ * each request...
108
+ * @param PyObject* os module
108 109
  */
109
-void update_pyenv(PyObject*, PyObject*);
110
-
111
-/**@brief Fetch python os.environ.__setitem__ & os.environ.clear()
112
- * @param PyObject** setitem
113
- * @param PyObject** clear
114
- */
115
-void get_py_setenv(PyObject**, PyObject**);
110
+void update_pyenv(PyObject*);
116 111
 
117 112
 void log_expt(int priority);
118 113
 

+ 6
- 2
src/responder.c View File

@@ -31,6 +31,7 @@ int responder_loop(char *py_entrypoint, unsigned int max_reqs,
31 31
 	unsigned int n_wrk;
32 32
 	int *wrk_pids;
33 33
 	int semid, err;
34
+	int status;
34 35
 
35 36
 	syslog(LOG_INFO, "Preparing workers");
36 37
 
@@ -57,10 +58,13 @@ int responder_loop(char *py_entrypoint, unsigned int max_reqs,
57 58
 	//Debug wait & exit
58 59
 	for(n_wrk=0; n_wrk != min_wrk; n_wrk++)
59 60
 	{
60
-		wait(NULL);
61
+		waitpid(wrk_pids[n_wrk], &status, 0);
62
+		syslog(LOG_DEBUG, "Child %d stopped with status %d",
63
+		       wrk_pids[n_wrk], status);
61 64
 	}
62 65
 		//printf("Content-Type: text/html\r\n\r\nHello world !\n");
63
-	return 0;
66
+	syslog(LOG_INFO,"Child workers stoped, stopping responder");
67
+	exit(0);
64 68
 }
65 69
 
66 70
 

Loading…
Cancel
Save